├── .gitignore
├── LICENSE
├── README.md
├── examples
├── iframe
│ ├── 2.html
│ ├── cloudflared-config.yml
│ ├── index.html
│ └── package.json
├── javascript
│ ├── build.html
│ ├── index.html
│ ├── package.json
│ └── script-tag-v2.html
├── next-frontend-functions
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── src
│ │ └── app
│ │ │ ├── audio-thread
│ │ │ ├── page.tsx
│ │ │ ├── primitives
│ │ │ │ └── page.tsx
│ │ │ └── webrtc-audio-runtime
│ │ │ │ └── page.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── suggestions
│ │ │ ├── MarkdownProvider
│ │ │ │ ├── Code.tsx
│ │ │ │ └── index.tsx
│ │ │ └── page.tsx
│ │ │ ├── thread-dialog
│ │ │ └── page.tsx
│ │ │ └── thread
│ │ │ ├── annotations
│ │ │ └── page.tsx
│ │ │ ├── customize-components
│ │ │ └── page.tsx
│ │ │ ├── image-upload
│ │ │ └── page.tsx
│ │ │ └── page.tsx
│ └── tsconfig.json
├── next-multi-assistant
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── app
│ │ │ ├── assistants
│ │ │ │ └── [assistantId]
│ │ │ │ │ ├── MarkdownProvider
│ │ │ │ │ ├── Code.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Menu.tsx
│ │ │ │ │ ├── layout.tsx
│ │ │ │ │ └── page.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ └── lib
│ │ │ └── assistants.ts
│ └── tsconfig.json
├── next
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── src
│ │ └── app
│ │ │ ├── api
│ │ │ └── openai
│ │ │ │ └── route.ts
│ │ │ ├── assistant-provider
│ │ │ └── page.tsx
│ │ │ ├── avatars
│ │ │ └── page.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── reset-thread
│ │ │ └── page.tsx
│ │ │ ├── suggestions
│ │ │ ├── MarkdownProvider
│ │ │ │ ├── Code.tsx
│ │ │ │ └── index.tsx
│ │ │ └── page.tsx
│ │ │ └── videos
│ │ │ ├── MarkdownProvider
│ │ │ ├── Link.tsx
│ │ │ └── index.tsx
│ │ │ └── page.tsx
│ └── tsconfig.json
├── standalone
│ ├── README.md
│ ├── example.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── src
│ │ ├── AudioThread.tsx
│ │ ├── AudioThreadDialog.tsx
│ │ ├── Thread.tsx
│ │ ├── ThreadDialog.tsx
│ │ └── components
│ │ │ └── Providers
│ │ │ ├── components
│ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ ├── reset.css
│ │ │ └── styles.css
│ ├── tsconfig.json
│ └── tsup.config.ts
└── vite-react-ts
│ ├── .gitignore
│ ├── README.md
│ ├── eslint.config.js
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ └── vite.svg
│ ├── src
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── package-lock.json
├── package.json
├── packages
├── javascript
│ ├── package.json
│ ├── postcss.config.js
│ ├── src
│ │ ├── AudioThread.tsx
│ │ ├── AudioThreadDialog.tsx
│ │ ├── AudioThreadDialogWebrtcAudioRuntime.tsx
│ │ ├── AudioThreadWebrtcAudioRuntime.tsx
│ │ ├── Thread.tsx
│ │ ├── ThreadAnnotationsComponentDisabled.tsx
│ │ ├── ThreadAnnotationsComponentSource.tsx
│ │ ├── ThreadDialog.tsx
│ │ ├── ThreadDialogAnnotationsComponentDisabled.tsx
│ │ ├── ThreadDialogAnnotationsComponentSource.tsx
│ │ ├── ThreadDialogFilesEnabled.tsx
│ │ ├── ThreadDialogFilesEnabledAnnotationsComponentDisabled.tsx
│ │ ├── ThreadDialogFilesEnabledAnnotationsComponentSource.tsx
│ │ ├── ThreadFilesEnabled.tsx
│ │ ├── ThreadFilesEnabledAnnotationsComponentDisabled.tsx
│ │ ├── ThreadFilesEnabledAnnotationsComponentSource.tsx
│ │ ├── components
│ │ │ └── Providers
│ │ │ │ ├── ThemeProvider.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── reset.css
│ │ │ │ └── styles.css
│ │ └── lib
│ │ │ └── superinterfaceContext
│ │ │ ├── currentScriptSuperinterfaceContext.ts
│ │ │ ├── index.ts
│ │ │ ├── isValidSuperinterfaceContext.ts
│ │ │ └── windowSuperinterfaceContext.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── react
│ ├── package.json
│ ├── src
│ │ ├── components
│ │ │ ├── annotations
│ │ │ │ ├── FilePathAnnotation.tsx
│ │ │ │ └── SourceAnnotation
│ │ │ │ │ ├── FileCitation
│ │ │ │ │ ├── Content.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── assistants
│ │ │ │ └── AssistantProvider
│ │ │ │ │ ├── Code.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── audioRuntimes
│ │ │ │ ├── TtsAudioRuntimeProvider.tsx
│ │ │ │ └── WebrtcAudioRuntimeProvider.tsx
│ │ │ ├── avatars
│ │ │ │ └── Avatar.tsx
│ │ │ ├── components
│ │ │ │ └── ComponentsProvider.tsx
│ │ │ ├── contents
│ │ │ │ ├── ImageFileContent.tsx
│ │ │ │ └── TextContent.tsx
│ │ │ ├── core
│ │ │ │ └── SuperinterfaceProvider
│ │ │ │ │ └── index.tsx
│ │ │ ├── functions
│ │ │ │ ├── Function
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── lib
│ │ │ │ │ │ └── title.ts
│ │ │ │ └── FunctionBase
│ │ │ │ │ ├── Content
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── lib
│ │ │ │ │ │ └── formattedJsonOrRaw.ts
│ │ │ │ │ ├── Icon.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── iconAvatars
│ │ │ │ └── IconAvatar.tsx
│ │ │ ├── imageAvatars
│ │ │ │ └── ImageAvatar
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── lib
│ │ │ │ │ └── optimizedSrc
│ │ │ │ │ ├── host.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── path.ts
│ │ │ ├── images
│ │ │ │ └── Image.tsx
│ │ │ ├── markdown
│ │ │ │ └── MarkdownProvider
│ │ │ │ │ └── index.tsx
│ │ │ ├── media
│ │ │ │ ├── MediaContainer.tsx
│ │ │ │ ├── PlayButton.tsx
│ │ │ │ ├── Time.tsx
│ │ │ │ └── VolumeButton.tsx
│ │ │ ├── messageGroups
│ │ │ │ └── MessageGroup
│ │ │ │ │ ├── AssistantAvatar.tsx
│ │ │ │ │ ├── Messages.tsx
│ │ │ │ │ ├── Name.tsx
│ │ │ │ │ ├── Root.tsx
│ │ │ │ │ ├── UserAvatar.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── messages
│ │ │ │ ├── MessageAttachments.tsx
│ │ │ │ └── MessageContent
│ │ │ │ │ ├── ContentPart
│ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── runSteps
│ │ │ │ ├── RunStep
│ │ │ │ │ ├── ToolCalls
│ │ │ │ │ │ ├── ToolCall
│ │ │ │ │ │ │ ├── CodeInterpreter.tsx
│ │ │ │ │ │ │ ├── Fallback.tsx
│ │ │ │ │ │ │ ├── FileSearch.tsx
│ │ │ │ │ │ │ ├── Fn.tsx
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ └── RunSteps
│ │ │ │ │ └── index.tsx
│ │ │ ├── skeletons
│ │ │ │ ├── MessagesSkeleton
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── StartingContentSkeleton
│ │ │ │ │ └── index.tsx
│ │ │ │ └── StartingSkeleton
│ │ │ │ │ └── index.tsx
│ │ │ ├── suggestions
│ │ │ │ └── Suggestions
│ │ │ │ │ ├── Content.tsx
│ │ │ │ │ ├── Item.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── textareas
│ │ │ │ └── TextareaBase
│ │ │ │ │ └── index.tsx
│ │ │ ├── threads
│ │ │ │ ├── AudioThread
│ │ │ │ │ ├── BarsVisualizer
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Form
│ │ │ │ │ │ ├── ActionButton
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── MicIcon.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Root
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Status
│ │ │ │ │ │ ├── StatusMessages.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Visualization
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── AudioThreadDialog
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Thread
│ │ │ │ │ ├── Message
│ │ │ │ │ │ ├── Attachments
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── Provider.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── MessageForm
│ │ │ │ │ │ ├── Field
│ │ │ │ │ │ │ ├── Control.tsx
│ │ │ │ │ │ │ ├── Files
│ │ │ │ │ │ │ │ ├── Control.tsx
│ │ │ │ │ │ │ │ ├── Preview.tsx
│ │ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── Root
│ │ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ │ └── lib
│ │ │ │ │ │ │ │ └── formOptions.ts
│ │ │ │ │ │ ├── Submit
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Messages
│ │ │ │ │ │ ├── Content
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── NextPageSkeleton.tsx
│ │ │ │ │ │ ├── Progress
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── Root
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Provider
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Root
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ └── ThreadDialog
│ │ │ │ │ ├── Close
│ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Content
│ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Provider
│ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Root
│ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Trigger
│ │ │ │ │ ├── Button.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ │ └── index.tsx
│ │ │ ├── toasts
│ │ │ │ └── ToastsProvider
│ │ │ │ │ ├── CustomToast.tsx
│ │ │ │ │ └── index.tsx
│ │ │ └── toolCalls
│ │ │ │ ├── CodeInterpreterToolCall.tsx
│ │ │ │ ├── FallbackToolCall.tsx
│ │ │ │ ├── FileSearchToolCall.tsx
│ │ │ │ ├── StartingToolCalls.tsx
│ │ │ │ └── ToolCallBase
│ │ │ │ ├── ToolCallIcon.tsx
│ │ │ │ ├── ToolCallTitle.tsx
│ │ │ │ └── index.tsx
│ │ ├── contexts
│ │ │ ├── assistants
│ │ │ │ ├── AssistantAvatarContext
│ │ │ │ │ └── index.tsx
│ │ │ │ └── AssistantNameContext
│ │ │ │ │ └── index.tsx
│ │ │ ├── components
│ │ │ │ └── ComponentsContext
│ │ │ │ │ └── index.tsx
│ │ │ ├── core
│ │ │ │ └── SuperinterfaceContext
│ │ │ │ │ └── index.tsx
│ │ │ ├── functions
│ │ │ │ └── FunctionComponentsContext
│ │ │ │ │ └── index.tsx
│ │ │ ├── markdown
│ │ │ │ └── MarkdownContext
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ ├── components
│ │ │ │ │ ├── Annotation
│ │ │ │ │ │ ├── AnnotationBase.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Code.tsx
│ │ │ │ │ ├── Img
│ │ │ │ │ │ ├── Audio
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── Video
│ │ │ │ │ │ │ ├── FullscreenButton.tsx
│ │ │ │ │ │ │ └── index.tsx
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── lib
│ │ │ │ │ │ │ ├── isAudioSrc.ts
│ │ │ │ │ │ │ └── isVideoSrc.ts
│ │ │ │ │ ├── Link.tsx
│ │ │ │ │ ├── ListItem.tsx
│ │ │ │ │ ├── OrderedList.tsx
│ │ │ │ │ ├── Paragraph.tsx
│ │ │ │ │ ├── Pre.tsx
│ │ │ │ │ ├── Strong.tsx
│ │ │ │ │ ├── UnorderedList.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ │ └── getRemarkPlugins.ts
│ │ │ ├── messages
│ │ │ │ ├── MessageContext
│ │ │ │ │ └── index.ts
│ │ │ │ └── MessageFormContext
│ │ │ │ │ └── index.ts
│ │ │ ├── threads
│ │ │ │ ├── AudioThreadContext
│ │ │ │ │ └── index.ts
│ │ │ │ └── ThreadDialogContext
│ │ │ │ │ └── index.ts
│ │ │ ├── toasts
│ │ │ │ └── ToastsContext
│ │ │ │ │ └── index.ts
│ │ │ └── users
│ │ │ │ └── UserAvatarContext
│ │ │ │ └── index.tsx
│ │ ├── hooks
│ │ │ ├── assistants
│ │ │ │ └── useAssistant
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ └── queryOptions
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── queryFn.ts
│ │ │ ├── audioRuntimes
│ │ │ │ ├── useTtsAudioRuntime
│ │ │ │ │ ├── blobToData.ts
│ │ │ │ │ └── index.ts
│ │ │ │ └── useWebrtcAudioRuntime
│ │ │ │ │ └── index.ts
│ │ │ ├── audioThreads
│ │ │ │ ├── useMessageAudio
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ │ ├── input.ts
│ │ │ │ │ │ └── isHtmlAudioSupported.ts
│ │ │ │ ├── useRecorder
│ │ │ │ │ └── index.ts
│ │ │ │ └── useStatus
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ └── statusMessages.ts
│ │ │ ├── components
│ │ │ │ └── useComponents.ts
│ │ │ ├── core
│ │ │ │ └── useSuperinterfaceContext
│ │ │ │ │ └── index.ts
│ │ │ ├── files
│ │ │ │ └── useCreateFile
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ └── mutationOptions
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── mutationFn
│ │ │ │ │ ├── body
│ │ │ │ │ ├── formData.ts
│ │ │ │ │ └── index.ts
│ │ │ │ │ └── index.ts
│ │ │ ├── markdown
│ │ │ │ └── useMarkdownContext
│ │ │ │ │ └── index.ts
│ │ │ ├── messageGroups
│ │ │ │ └── useMessageGroups
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ └── messageGroups
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── newGroup
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── newGroupItem.ts
│ │ │ ├── messages
│ │ │ │ ├── useCreateMessage
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lib
│ │ │ │ │ │ └── mutationOptions
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── mutationFn
│ │ │ │ │ │ ├── body.ts
│ │ │ │ │ │ ├── handleResponse
│ │ │ │ │ │ │ ├── handlers
│ │ │ │ │ │ │ │ ├── extendMessage.ts
│ │ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ │ ├── threadCreated.ts
│ │ │ │ │ │ │ │ ├── threadMessageCompleted.ts
│ │ │ │ │ │ │ │ ├── threadMessageCreated.ts
│ │ │ │ │ │ │ │ ├── threadMessageDelta.ts
│ │ │ │ │ │ │ │ ├── threadRunCreated.ts
│ │ │ │ │ │ │ │ ├── threadRunFailed.ts
│ │ │ │ │ │ │ │ ├── threadRunRequiresAction.ts
│ │ │ │ │ │ │ │ ├── threadRunStepCompleted.ts
│ │ │ │ │ │ │ │ ├── threadRunStepCreated.ts
│ │ │ │ │ │ │ │ └── threadRunStepDelta.ts
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── onMutate
│ │ │ │ │ │ ├── data.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ ├── useIsMutatingMessage
│ │ │ │ │ └── index.ts
│ │ │ │ ├── useLatestMessage
│ │ │ │ │ └── index.ts
│ │ │ │ ├── useMessageContext
│ │ │ │ │ └── index.ts
│ │ │ │ ├── useMessageFormContext
│ │ │ │ │ └── index.ts
│ │ │ │ └── useMessages
│ │ │ │ │ └── index.tsx
│ │ │ ├── misc
│ │ │ │ ├── useInfiniteScroll
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useInterval.ts
│ │ │ │ ├── usePermission
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── util.ts
│ │ │ │ ├── usePrevious.ts
│ │ │ │ └── useThrottledEffect
│ │ │ │ │ └── index.tsx
│ │ │ ├── threads
│ │ │ │ ├── useAudioThreadContext
│ │ │ │ │ └── index.ts
│ │ │ │ ├── useThreadContext
│ │ │ │ │ └── index.ts
│ │ │ │ └── useThreadDialogContext
│ │ │ │ │ └── index.ts
│ │ │ └── toasts
│ │ │ │ └── useToasts
│ │ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── lib
│ │ │ ├── ai
│ │ │ │ └── index.ts
│ │ │ ├── enums
│ │ │ │ └── index.ts
│ │ │ ├── errors
│ │ │ │ └── createMessageDefaultOnError.ts
│ │ │ ├── iconAvatars
│ │ │ │ └── iconAvatarComponents.ts
│ │ │ ├── iframes
│ │ │ │ └── isIframe.ts
│ │ │ ├── markdown
│ │ │ │ └── escapeInvalidTagNames.ts
│ │ │ ├── messages
│ │ │ │ ├── createMessageResponse
│ │ │ │ │ ├── actionsStream.ts
│ │ │ │ │ ├── handleStream.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── extendMessage.ts
│ │ │ │ ├── messagesQueryOptions.ts
│ │ │ │ ├── messagesResponse
│ │ │ │ │ ├── data
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── messages
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── runMessages
│ │ │ │ │ │ │ ├── getLatestRun.ts
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── hasNextPage.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── limit.ts
│ │ │ │ ├── order.ts
│ │ │ │ └── serializeMessage.ts
│ │ │ ├── misc
│ │ │ │ └── merge
│ │ │ │ │ ├── customizer.ts
│ │ │ │ │ └── index.ts
│ │ │ ├── optimistic
│ │ │ │ ├── isOptimistic.ts
│ │ │ │ └── optimisticId.ts
│ │ │ ├── recma
│ │ │ │ └── recmaFallbackComponentPlugin.ts
│ │ │ ├── remark
│ │ │ │ ├── remarkAnnotation.ts
│ │ │ │ └── remarkPureLiteralPlugin.ts
│ │ │ ├── runSteps
│ │ │ │ ├── getRunSteps
│ │ │ │ │ └── index.ts
│ │ │ │ └── serializeRunStep.ts
│ │ │ ├── runs
│ │ │ │ └── serializeRun.ts
│ │ │ ├── streams
│ │ │ │ └── enqueueJson.ts
│ │ │ ├── superinterfaceCloud
│ │ │ │ └── baseUrl.ts
│ │ │ ├── threadIdStorage
│ │ │ │ ├── cookieOptions
│ │ │ │ │ ├── get.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── remove.ts
│ │ │ │ │ └── set.ts
│ │ │ │ ├── key.ts
│ │ │ │ └── localStorageOptions
│ │ │ │ │ ├── get.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── remove.ts
│ │ │ │ │ └── set.ts
│ │ │ └── threads
│ │ │ │ └── queryOptions
│ │ │ │ ├── index.ts
│ │ │ │ └── variableParams.ts
│ │ ├── server.ts
│ │ ├── types
│ │ │ └── index.ts
│ │ └── utils.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
└── root-element
│ ├── package.json
│ ├── src
│ ├── index.ts
│ └── lib
│ │ └── rootElement
│ │ ├── index.ts
│ │ ├── manualElement.ts
│ │ └── scriptTagId.ts
│ ├── tsconfig.json
│ └── tsup.config.ts
├── tsconfig.json
└── turbo.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .turbo
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Supercorp
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Superinterface
4 |
5 | Superinterface is an AI assistants library for building AI capabilities into your app or website.
6 | You use React components and hooks to build AI-first assistants-based interfaces like chats and wizards.
7 |
8 | **Docs are in the process of being written.**
9 |
10 | More info about the project: https://superinterface.ai
11 |
12 | Examples are hosted here: https://examples-next.superinterface.ai (see code [here](https://github.com/supercorp-ai/superinterface/tree/main/examples/next))
13 |
--------------------------------------------------------------------------------
/examples/iframe/cloudflared-config.yml:
--------------------------------------------------------------------------------
1 | tunnel: 7171dd8d-9e40-45e0-93a8-40f2ca04ecc1
2 | credentials-file: /Users/domas/.cloudflared/7171dd8d-9e40-45e0-93a8-40f2ca04ecc1.json
3 | warp-routing:
4 | enabled: true
5 | ingress:
6 | - hostname: superinterface-examples-iframe.supercorp.ai
7 | service: http://localhost:3006
8 | - service: http_status:404
9 |
--------------------------------------------------------------------------------
/examples/iframe/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@superinterface/iframe-example",
3 | "version": "1.0.0",
4 | "keywords": [],
5 | "license": "ISC",
6 | "scripts": {
7 | "serve": "npx serve . -p 3006",
8 | "tunnel": "cloudflared tunnel --config cloudflared-config.yml run"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/javascript/build.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
15 | Hello JavaScript build!
16 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/javascript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@superinterface/javascript-example",
3 | "version": "1.0.0",
4 | "keywords": [],
5 | "license": "ISC",
6 | "scripts": {
7 | "serve": "npx serve . -p 3006"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/javascript/script-tag-v2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
17 |
18 |
19 | Hello JavaScript v2!
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@superinterface/react": "file:../../packages/react",
13 | "@tanstack/react-query": "file:../../packages/react/node_modules/@tanstack/react-query",
14 | "next": "14.2.5",
15 | "react": "^18",
16 | "react-dom": "^18"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20",
20 | "@types/react": "^18",
21 | "@types/react-dom": "^18",
22 | "eslint": "^8",
23 | "eslint-config-next": "14.2.5",
24 | "typescript": "^5"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supercorp-ai/superinterface/70b16812be1d48622c52768d9269190caa826df8/examples/next-frontend-functions/src/app/favicon.ico
--------------------------------------------------------------------------------
/examples/next-frontend-functions/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/src/app/suggestions/MarkdownProvider/Code.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | useMarkdownContext,
4 | useMessageContext,
5 | Suggestions,
6 | } from '@superinterface/react'
7 |
8 | export const Code = ({
9 | children,
10 | className,
11 | markdownContext,
12 | }: {
13 | children: string,
14 | className: string,
15 | markdownContext: ReturnType
16 | }) => {
17 | const messageContext = useMessageContext()
18 |
19 | const isAssistantMessage = useMemo(() => (
20 | messageContext.message?.role === 'assistant'
21 | ), [messageContext])
22 |
23 | if (!isAssistantMessage || className !== 'language-suggestions') {
24 | return markdownContext.components.code({ children, className })
25 | }
26 |
27 | return {children}
28 | }
29 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/src/app/suggestions/MarkdownProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | MarkdownProvider as SuperinterfaceMarkdownProvider,
4 | useMarkdownContext,
5 | } from '@superinterface/react'
6 | import { Code } from './Code'
7 |
8 | type Args = {
9 | children: React.ReactNode
10 | }
11 |
12 | export const MarkdownProvider = ({
13 | children,
14 | }: Args) => {
15 | const markdownContext = useMarkdownContext()
16 |
17 | const components = useMemo(() => ({
18 | code: (props: JSX.IntrinsicElements['code']) => (
19 | // @ts-ignore-next-line
20 |
24 | ),
25 | }), [markdownContext])
26 |
27 | return (
28 |
32 | {children}
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/examples/next-frontend-functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-multi-assistant",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@radix-ui/themes": "^3.1.3",
13 | "@superinterface/react": "^2.19.3",
14 | "@tanstack/react-query": "^5.54.1",
15 | "next": "14.2.8",
16 | "react": "^18",
17 | "react-dom": "^18"
18 | },
19 | "devDependencies": {
20 | "@types/node": "^20",
21 | "@types/react": "^18",
22 | "@types/react-dom": "^18",
23 | "eslint": "^8",
24 | "eslint-config-next": "14.2.8",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/assistants/[assistantId]/MarkdownProvider/Code.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | useMarkdownContext,
4 | useMessageContext,
5 | Suggestions,
6 | } from '@superinterface/react'
7 |
8 | export const Code = ({
9 | children,
10 | className,
11 | markdownContext,
12 | }: {
13 | children: string,
14 | className: string,
15 | markdownContext: ReturnType
16 | }) => {
17 | const messageContext = useMessageContext()
18 |
19 | const isAssistantMessage = useMemo(() => (
20 | messageContext.message?.role === 'assistant'
21 | ), [messageContext])
22 |
23 | if (!isAssistantMessage || className !== 'language-suggestions') {
24 | return markdownContext.components.code({ children, className })
25 | }
26 |
27 | return {children}
28 | }
29 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/assistants/[assistantId]/MarkdownProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | MarkdownProvider as SuperinterfaceMarkdownProvider,
4 | useMarkdownContext,
5 | } from '@superinterface/react'
6 | import { Code } from './Code'
7 |
8 | type Args = {
9 | children: React.ReactNode
10 | }
11 |
12 | export const MarkdownProvider = ({
13 | children,
14 | }: Args) => {
15 | const markdownContext = useMarkdownContext()
16 |
17 | const components = useMemo(() => ({
18 | code: (props: JSX.IntrinsicElements['code']) => (
19 | // @ts-expect-error broad types
20 |
24 | ),
25 | }), [markdownContext])
26 |
27 | return (
28 |
32 | {children}
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/assistants/[assistantId]/Menu.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import {
3 | Flex,
4 | Text,
5 | Card,
6 | } from '@radix-ui/themes'
7 | import { assistants } from '@/lib/assistants'
8 |
9 | const Item = ({
10 | assistant,
11 | }: {
12 | assistant: {
13 | id: string
14 | name: string
15 | },
16 | }) => (
17 |
21 |
25 |
29 |
34 | {assistant.name}
35 |
36 |
37 |
38 |
39 | )
40 |
41 | export const Menu = () => (
42 |
54 | {assistants.map((assistant) => (
55 |
59 | ))}
60 |
61 | )
62 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supercorp-ai/superinterface/70b16812be1d48622c52768d9269190caa826df8/examples/next-multi-assistant/src/app/favicon.ico
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/globals.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, Helvetica, sans-serif;
3 | -webkit-font-smoothing: antialiased;
4 | -moz-osx-font-smoothing: grayscale;
5 | }
6 |
7 | * {
8 | box-sizing: border-box;
9 | padding: 0;
10 | margin: 0;
11 | }
12 |
13 | a {
14 | color: inherit;
15 | text-decoration: none;
16 | }
17 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from 'next'
2 | import { Inter } from 'next/font/google'
3 | import './globals.css'
4 | import '@radix-ui/themes/styles.css'
5 |
6 | const inter = Inter({ subsets: ['latin'] })
7 |
8 | export const metadata: Metadata = {
9 | title: 'Create Next App',
10 | description: 'Generated by create next app',
11 | }
12 |
13 | export default function RootLayout({
14 | children,
15 | }: Readonly<{
16 | children: React.ReactNode
17 | }>) {
18 | return (
19 |
20 |
21 | {children}
22 |
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { redirect } from 'next/navigation'
2 | import { assistants } from '@/lib/assistants'
3 |
4 | export default function Page() {
5 | redirect(`/assistants/${assistants[0].id}`)
6 | }
7 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/src/lib/assistants.ts:
--------------------------------------------------------------------------------
1 | export const assistants = [
2 | {
3 | id: '26518c2b-07e4-44a7-bc62-36b0b3922bc7',
4 | name: 'Next Example Assistant',
5 | },
6 | {
7 | id: '1bcb2f56-6aa5-41bd-8e71-e077331520ce',
8 | name: 'Next Example Suggestions Assistant',
9 | },
10 | ]
11 |
--------------------------------------------------------------------------------
/examples/next-multi-assistant/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/next/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/next/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 | .env
38 |
--------------------------------------------------------------------------------
/examples/next/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@superinterface/react": "^2.25.2",
13 | "@tanstack/react-query": "^5.59.20",
14 | "js-cookie": "^3.0.5",
15 | "next": "14.2.5",
16 | "react": "^18",
17 | "react-dom": "^18"
18 | },
19 | "devDependencies": {
20 | "@types/js-cookie": "^3.0.6",
21 | "@types/node": "^20",
22 | "@types/react": "^18",
23 | "@types/react-dom": "^18",
24 | "eslint": "^8",
25 | "eslint-config-next": "14.2.5",
26 | "typescript": "^5"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/next/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/next/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/next/src/app/api/openai/route.ts:
--------------------------------------------------------------------------------
1 | import { type NextRequest, NextResponse } from 'next/server'
2 | import OpenAI from 'openai'
3 | import {
4 | messagesResponse,
5 | } from '@superinterface/react/server'
6 |
7 | export const GET = async () => {
8 | try {
9 | const client = new OpenAI({
10 | apiKey: 'sk-proj-11111111111111111111',
11 | })
12 |
13 | const pageParam = null
14 |
15 | const r = await messagesResponse({
16 | threadId: 'thread_BWZSFgxf4o7x1psewUjwhitt',
17 | client,
18 | ...(pageParam ? { pageParam } : {}),
19 | })
20 |
21 | return NextResponse.json(r)
22 | } catch (e) {
23 | return NextResponse.json({
24 | error: 'Provide a valid API key',
25 | })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/next/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supercorp-ai/superinterface/70b16812be1d48622c52768d9269190caa826df8/examples/next/src/app/favicon.ico
--------------------------------------------------------------------------------
/examples/next/src/app/globals.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | padding: 0;
4 | margin: 0;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/next/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/examples/next/src/app/suggestions/MarkdownProvider/Code.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | useMarkdownContext,
4 | useMessageContext,
5 | Suggestions,
6 | } from '@superinterface/react'
7 |
8 | export const Code = ({
9 | children,
10 | className,
11 | markdownContext,
12 | }: {
13 | children: string,
14 | className: string,
15 | markdownContext: ReturnType
16 | }) => {
17 | const messageContext = useMessageContext()
18 |
19 | const isAssistantMessage = useMemo(() => (
20 | messageContext.message?.role === 'assistant'
21 | ), [messageContext])
22 |
23 | if (!isAssistantMessage || className !== 'language-suggestions') {
24 | return markdownContext.components.code({ children, className })
25 | }
26 |
27 | return {children}
28 | }
29 |
--------------------------------------------------------------------------------
/examples/next/src/app/suggestions/MarkdownProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | MarkdownProvider as SuperinterfaceMarkdownProvider,
4 | useMarkdownContext,
5 | } from '@superinterface/react'
6 | import { Code } from './Code'
7 |
8 | type Args = {
9 | children: React.ReactNode
10 | }
11 |
12 | export const MarkdownProvider = ({
13 | children,
14 | }: Args) => {
15 | const markdownContext = useMarkdownContext()
16 |
17 | const components = useMemo(() => ({
18 | code: (props: JSX.IntrinsicElements['code']) => (
19 | // @ts-ignore-next-line
20 |
24 | ),
25 | }), [markdownContext])
26 |
27 | return (
28 |
32 | {children}
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/examples/next/src/app/videos/MarkdownProvider/Link.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | useMarkdownContext,
3 | } from '@superinterface/react'
4 |
5 | const isYoutube = ({ url }: { url: string }): boolean => {
6 | try {
7 | const parsedUrl = new URL(url)
8 | return parsedUrl.hostname === 'www.youtube.com' || parsedUrl.hostname === 'youtube.com' || parsedUrl.hostname === 'youtu.be'
9 | } catch (e) {
10 | return false
11 | }
12 | }
13 |
14 |
15 | export const Link = ({
16 | children,
17 | href,
18 | markdownContext,
19 | }: {
20 | children?: React.ReactNode,
21 | href?: string,
22 | markdownContext: ReturnType
23 | }) => {
24 | if (!href || !isYoutube({ url: href })) {
25 | return markdownContext.components.a({ children, href })
26 | }
27 |
28 | return (
29 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/examples/next/src/app/videos/MarkdownProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | MarkdownProvider as SuperinterfaceMarkdownProvider,
4 | useMarkdownContext,
5 | } from '@superinterface/react'
6 | import { Link } from './Link'
7 |
8 | type Args = {
9 | children: React.ReactNode
10 | }
11 |
12 | export const MarkdownProvider = ({
13 | children,
14 | }: Args) => {
15 | const markdownContext = useMarkdownContext()
16 |
17 | const components = useMemo(() => ({
18 | a: (props: JSX.IntrinsicElements['a']) => ,
19 | }), [markdownContext])
20 |
21 | return (
22 |
26 | {children}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/examples/next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/standalone/README.md:
--------------------------------------------------------------------------------
1 | # Standalone bundle example with videos & audio
2 |
3 | This is an example of a custom setup that exports a standalone JavaScript bundle with markdown video and audio support.
4 |
5 | Markdown video follows classic image syntax with video being detected by the file extension. For example, ``.
6 |
7 | Audio is similar to video, but with the audio file extension. For example, ``.
8 |
9 | ## Getting Started
10 |
11 | Install the dependencies:
12 |
13 | ```bash
14 | npm install
15 | ```
16 |
17 | Then build the project:
18 |
19 | ```bash
20 | npm run build
21 | ```
22 |
23 | Then you can open `example.html` in your browser and you’ll see Superinterface embedded in multiple ways.
24 |
25 | ## Deployment
26 |
27 | Once you have built the project, decide what kind of type of Superinterface you want to display.
28 |
29 | If it’s inline - use Thread.global.js, if it’s a dialog - use ThreadDialog.global.js. If it’s voice - use AudioThread.global.js.
30 |
31 | Then copy the file (for example, Thread.global.js) to your project and include it in your HTML file.
32 |
33 | If it’s Thread, by default it will render into the div with id thread-root. You can customize by directly editing Thread.tsx file and then running `npm run build` again.
34 |
--------------------------------------------------------------------------------
/examples/standalone/example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 | Hello custom code!
11 |
15 | Some footer
16 |
20 | Another footer
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/examples/standalone/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "superinterface-standalone-example",
3 | "version": "1.0.0",
4 | "main": "./dist/ThreadDialog.js",
5 | "module": "./dist/ThreadDialog.mjs",
6 | "types": "./dist/ThreadDialog.d.ts",
7 | "exports": {
8 | ".": "./dist/ThreadDialog.js",
9 | "./*": "./dist/*.js",
10 | "./types": "./dist/types/ThreadDialog.js"
11 | },
12 | "files": [
13 | "dist"
14 | ],
15 | "keywords": [],
16 | "license": "ISC",
17 | "scripts": {
18 | "build": "tsup",
19 | "metafile": "tsup --metafile",
20 | "serve": "npx serve dist -p 3005 --cors"
21 | },
22 | "dependencies": {
23 | "@radix-ui/react-icons": "^1.3.0",
24 | "@radix-ui/themes": "3.1.4",
25 | "@superinterface/react": "2.24.1",
26 | "@tanstack/react-query": "^5.59.16",
27 | "@vidstack/react": "^1.12.12",
28 | "react": "^18.3.1",
29 | "react-dom": "^18.3.1",
30 | "video-extensions": "^2.0.0"
31 | },
32 | "devDependencies": {
33 | "@types/react": "^18.3.12",
34 | "@types/react-dom": "^18.3.1",
35 | "autoprefixer": "^10.4.20",
36 | "postcss": "^8.4.47",
37 | "postcss-import": "^16.1.0",
38 | "read-cache": "^1.0.0",
39 | "resolve": "^1.22.8",
40 | "tsup": "^8.3.5"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/standalone/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import')(),
4 | require('autoprefixer')(),
5 | ],
6 | }
7 |
--------------------------------------------------------------------------------
/examples/standalone/src/AudioThread.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | AudioThread,
4 | } from '@superinterface/react'
5 | import { Providers } from '@/components/Providers'
6 |
7 | const element = document.createElement('div')
8 | element.classList.add('superinterface')
9 | document.querySelector('#audio-thread-root')?.appendChild(element)
10 |
11 | const root = createRoot(element)
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/examples/standalone/src/AudioThreadDialog.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | AudioThreadDialog,
4 | } from '@superinterface/react'
5 | import { Providers } from '@/components/Providers'
6 |
7 | const element = document.createElement('div')
8 | element.classList.add('superinterface')
9 | document.body.appendChild(element)
10 |
11 | const root = createRoot(element)
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/examples/standalone/src/Thread.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | } from '@superinterface/react'
5 | import { Providers } from '@/components/Providers'
6 |
7 | const element = document.createElement('div')
8 | element.classList.add('superinterface')
9 | document.querySelector('#thread-root')?.appendChild(element)
10 |
11 | const root = createRoot(element)
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/examples/standalone/src/ThreadDialog.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | ThreadDialog,
4 | } from '@superinterface/react'
5 | import { Providers } from '@/components/Providers'
6 |
7 | const element = document.createElement('div')
8 | element.classList.add('superinterface')
9 | document.body.appendChild(element)
10 |
11 | const root = createRoot(element)
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/examples/standalone/src/components/Providers/components/index.tsx:
--------------------------------------------------------------------------------
1 | export const components = {
2 | }
3 |
--------------------------------------------------------------------------------
/examples/standalone/src/components/Providers/reset.css:
--------------------------------------------------------------------------------
1 | .superinterface * {
2 | margin: 0;
3 | padding: 0;
4 | font-family: inherit;
5 | font-weight: inherit;
6 | font-size: inherit;
7 | text-decoration: none;
8 | color: inherit;
9 | background-color: transparent;
10 | border: 0;
11 | outline: 0;
12 | text-transform: none;
13 | -webkit-appearance: inherit;
14 | box-sizing: border-box;
15 | /* Safari 12 calc + vw resize fix */
16 | min-height: 0vw;
17 | white-space: normal;
18 | }
19 |
--------------------------------------------------------------------------------
/examples/standalone/src/components/Providers/styles.css:
--------------------------------------------------------------------------------
1 | @import './reset.css';
2 | @import '@radix-ui/themes/styles.css';
3 | @import '@vidstack/react/player/styles/base.css';
4 |
5 | .superinterface {
6 | display: flex;
7 | flex-grow: 1;
8 | }
9 |
10 | .superinterface .radix-themes {
11 | min-height: inherit;
12 | display: flex;
13 | flex-grow: 1;
14 | --default-font-family: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
15 | --cursor-button: pointer;
16 | --cursor-menu-item: pointer;
17 | }
18 |
--------------------------------------------------------------------------------
/examples/standalone/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "declaration": false,
6 | "noEmit": false,
7 | "paths": {
8 | "@/*": ["./src/*"]
9 | }
10 | },
11 | "include": ["src"],
12 | "exclude": ["node_modules", "dist"]
13 | }
14 |
--------------------------------------------------------------------------------
/examples/standalone/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | define: {
5 | 'process.env.NODE_ENV': JSON.stringify('production'),
6 | },
7 | entry: [
8 | 'src/Thread.tsx',
9 | 'src/AudioThread.tsx',
10 | 'src/ThreadDialog.tsx',
11 | 'src/AudioThreadDialog.tsx',
12 | ],
13 | sourcemap: true,
14 | clean: true,
15 | shims: true,
16 | platform: 'browser',
17 | minify: true,
18 | format: [
19 | 'iife',
20 | 'cjs',
21 | 'esm'
22 | ],
23 | injectStyle: true,
24 | dts: false,
25 | noExternal: [
26 | /(.*)/
27 | ],
28 | })
29 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import reactHooks from 'eslint-plugin-react-hooks'
4 | import reactRefresh from 'eslint-plugin-react-refresh'
5 | import tseslint from 'typescript-eslint'
6 |
7 | export default tseslint.config(
8 | { ignores: ['dist'] },
9 | {
10 | extends: [js.configs.recommended, ...tseslint.configs.recommended],
11 | files: ['**/*.{ts,tsx}'],
12 | languageOptions: {
13 | ecmaVersion: 2020,
14 | globals: globals.browser,
15 | },
16 | plugins: {
17 | 'react-hooks': reactHooks,
18 | 'react-refresh': reactRefresh,
19 | },
20 | rules: {
21 | ...reactHooks.configs.recommended.rules,
22 | 'react-refresh/only-export-components': [
23 | 'warn',
24 | { allowConstantExport: true },
25 | ],
26 | },
27 | },
28 | )
29 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-react-ts",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@superinterface/react": "^2.25.1",
14 | "@tanstack/react-query": "^5.59.20",
15 | "react": "^18.3.1",
16 | "react-dom": "^18.3.1"
17 | },
18 | "devDependencies": {
19 | "@eslint/js": "^9.14.0",
20 | "@types/react": "^18.3.12",
21 | "@types/react-dom": "^18.3.1",
22 | "@vitejs/plugin-react": "^4.3.3",
23 | "eslint": "^9.14.0",
24 | "eslint-plugin-react-hooks": "^5.0.0",
25 | "eslint-plugin-react-refresh": "^0.4.14",
26 | "globals": "^15.12.0",
27 | "typescript": "~5.6.3",
28 | "typescript-eslint": "^8.13.0",
29 | "vite": "^5.4.10"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | font-synthesis: none;
7 | text-rendering: optimizeLegibility;
8 | -webkit-font-smoothing: antialiased;
9 | -moz-osx-font-smoothing: grayscale;
10 | }
11 |
12 | a {
13 | font-weight: 500;
14 | color: #646cff;
15 | text-decoration: inherit;
16 | }
17 |
18 | a:hover {
19 | color: #535bf2;
20 | }
21 |
22 | body {
23 | margin: 0;
24 | padding: 0;
25 | }
26 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App.tsx'
5 |
6 | createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4 | "target": "ES2020",
5 | "useDefineForClassFields": true,
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "module": "ESNext",
8 | "skipLibCheck": true,
9 |
10 | /* Bundler mode */
11 | "moduleResolution": "Bundler",
12 | "allowImportingTsExtensions": true,
13 | "isolatedModules": true,
14 | "moduleDetection": "force",
15 | "noEmit": true,
16 | "jsx": "react-jsx",
17 |
18 | /* Linting */
19 | "strict": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "noUncheckedSideEffectImports": true
24 | },
25 | "include": ["src"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | { "path": "./tsconfig.app.json" },
5 | { "path": "./tsconfig.node.json" }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4 | "target": "ES2022",
5 | "lib": ["ES2023"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "Bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedSideEffectImports": true
22 | },
23 | "include": ["vite.config.ts"]
24 | }
25 |
--------------------------------------------------------------------------------
/examples/vite-react-ts/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@superinterface/superinterface",
3 | "private": true,
4 | "workspaces": [
5 | "packages/*"
6 | ],
7 | "scripts": {
8 | "build": "turbo run build --force",
9 | "dev": "turbo run build --force -- --watch"
10 | },
11 | "dependencies": {
12 | "react-error-boundary": "^4.1.2",
13 | "typescript": "^5.6.3"
14 | },
15 | "devDependencies": {
16 | "@swc/core": "^1.9.2",
17 | "turbo": "^2.3.0"
18 | },
19 | "packageManager": "npm@10.2.0"
20 | }
21 |
--------------------------------------------------------------------------------
/packages/javascript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@superinterface/javascript",
3 | "version": "3.7.1",
4 | "main": "./dist/ThreadDialog.js",
5 | "module": "./dist/ThreadDialog.mjs",
6 | "types": "./dist/ThreadDialog.d.ts",
7 | "exports": {
8 | ".": "./dist/ThreadDialog.js",
9 | "./*": "./dist/*.js",
10 | "./types": "./dist/types/ThreadDialog.js"
11 | },
12 | "files": [
13 | "dist"
14 | ],
15 | "keywords": [],
16 | "license": "ISC",
17 | "scripts": {
18 | "build": "NODE_OPTIONS='--max-old-space-size=8192' tsup",
19 | "metafile": "tsup --metafile",
20 | "serve": "npx serve dist -p 3005 --cors"
21 | },
22 | "dependencies": {
23 | "@radix-ui/react-icons": "^1.3.2",
24 | "@radix-ui/themes": "3.2.1",
25 | "@superinterface/react": "*",
26 | "@superinterface/root-element": "*",
27 | "@tanstack/react-query": "^5.71.10",
28 | "react": "19.1.0",
29 | "react-dom": "19.1.0"
30 | },
31 | "devDependencies": {
32 | "@types/react": "^19.1.0",
33 | "@types/react-dom": "^19.1.1",
34 | "autoprefixer": "^10.4.21",
35 | "postcss": "^8.5.3",
36 | "postcss-import": "^16.1.0",
37 | "read-cache": "^1.0.0",
38 | "resolve": "^1.22.10",
39 | "tsup": "^8.4.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/javascript/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import')(),
4 | require('autoprefixer')(),
5 | ],
6 | }
7 |
--------------------------------------------------------------------------------
/packages/javascript/src/AudioThread.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | AudioThread,
4 | } from '@superinterface/react'
5 | import { rootElement } from '@superinterface/root-element'
6 | import { Providers } from '@/components/Providers'
7 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
8 |
9 | const currentScript = document.currentScript
10 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
11 | const root = createRoot(rootElement({ currentScript }))
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/javascript/src/AudioThreadDialog.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | AudioThreadDialog,
4 | } from '@superinterface/react'
5 | import { rootElement } from '@superinterface/root-element'
6 | import { Providers } from '@/components/Providers'
7 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
8 |
9 | const currentScript = document.currentScript
10 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
11 | const root = createRoot(rootElement({ currentScript }))
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/javascript/src/AudioThreadDialogWebrtcAudioRuntime.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | AudioThreadDialog,
4 | WebrtcAudioRuntimeProvider,
5 | } from '@superinterface/react'
6 | import { rootElement } from '@superinterface/root-element'
7 | import { Providers } from '@/components/Providers'
8 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
9 |
10 | const currentScript = document.currentScript
11 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
12 | const root = createRoot(rootElement({ currentScript }))
13 |
14 | root.render(
15 |
16 |
17 |
18 |
19 |
20 | )
21 |
--------------------------------------------------------------------------------
/packages/javascript/src/AudioThreadWebrtcAudioRuntime.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | AudioThread,
4 | WebrtcAudioRuntimeProvider,
5 | } from '@superinterface/react'
6 | import { rootElement } from '@superinterface/root-element'
7 | import { Providers } from '@/components/Providers'
8 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
9 |
10 | const currentScript = document.currentScript
11 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
12 | const root = createRoot(rootElement({ currentScript }))
13 |
14 | root.render(
15 |
16 |
17 |
18 |
19 |
20 | )
21 |
--------------------------------------------------------------------------------
/packages/javascript/src/Thread.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | } from '@superinterface/react'
5 | import { rootElement } from '@superinterface/root-element'
6 | import { Providers } from '@/components/Providers'
7 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
8 |
9 | const currentScript = document.currentScript
10 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
11 | const root = createRoot(rootElement({ currentScript }))
12 |
13 | root.render(
14 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadAnnotationsComponentDisabled.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | MarkdownProvider,
5 | } from '@superinterface/react'
6 | import { rootElement } from '@superinterface/root-element'
7 | import { Providers } from '@/components/Providers'
8 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
9 |
10 | const currentScript = document.currentScript
11 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
12 | const root = createRoot(rootElement({ currentScript }))
13 |
14 | root.render(
15 |
16 | null,
19 | }}
20 | >
21 |
22 |
23 |
24 | )
25 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadAnnotationsComponentSource.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | MarkdownProvider,
5 | SourceAnnotation,
6 | } from '@superinterface/react'
7 | import { rootElement } from '@superinterface/root-element'
8 | import { Providers } from '@/components/Providers'
9 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
10 |
11 | const currentScript = document.currentScript
12 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
13 | const root = createRoot(rootElement({ currentScript }))
14 |
15 | root.render(
16 |
17 |
22 |
23 |
24 |
25 | )
26 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadDialog.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | ThreadDialog,
4 | } from '@superinterface/react'
5 | import { rootElement } from '@superinterface/root-element'
6 | import { Providers } from '@/components/Providers'
7 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
8 |
9 | const currentScript = document.currentScript
10 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
11 | const root = createRoot(rootElement({ currentScript }))
12 |
13 | root.render(
14 |
17 |
18 |
19 | )
20 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadDialogAnnotationsComponentDisabled.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | ThreadDialog,
4 | MarkdownProvider,
5 | } from '@superinterface/react'
6 | import { rootElement } from '@superinterface/root-element'
7 | import { Providers } from '@/components/Providers'
8 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
9 |
10 | const currentScript = document.currentScript
11 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
12 | const root = createRoot(rootElement({ currentScript }))
13 |
14 | root.render(
15 |
18 | null,
21 | }}
22 | >
23 |
24 |
25 |
26 | )
27 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadDialogAnnotationsComponentSource.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | ThreadDialog,
4 | MarkdownProvider,
5 | SourceAnnotation,
6 | } from '@superinterface/react'
7 | import { rootElement } from '@superinterface/root-element'
8 | import { Providers } from '@/components/Providers'
9 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
10 |
11 | const currentScript = document.currentScript
12 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
13 | const root = createRoot(rootElement({ currentScript }))
14 |
15 | root.render(
16 |
19 |
24 |
25 |
26 |
27 | )
28 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadFilesEnabled.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | } from '@superinterface/react'
5 | import { rootElement } from '@superinterface/root-element'
6 | import { Providers } from '@/components/Providers'
7 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
8 |
9 | const currentScript = document.currentScript
10 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
11 | const root = createRoot(rootElement({ currentScript }))
12 |
13 | root.render(
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadFilesEnabledAnnotationsComponentDisabled.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | MarkdownProvider,
5 | } from '@superinterface/react'
6 | import { rootElement } from '@superinterface/root-element'
7 | import { Providers } from '@/components/Providers'
8 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
9 |
10 | const currentScript = document.currentScript
11 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
12 | const root = createRoot(rootElement({ currentScript }))
13 |
14 | root.render(
15 |
16 | null,
19 | }}
20 | >
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 |
--------------------------------------------------------------------------------
/packages/javascript/src/ThreadFilesEnabledAnnotationsComponentSource.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client'
2 | import {
3 | Thread,
4 | MarkdownProvider,
5 | SourceAnnotation,
6 | } from '@superinterface/react'
7 | import { rootElement } from '@superinterface/root-element'
8 | import { Providers } from '@/components/Providers'
9 | import { superinterfaceContext as getSuperinterfaceContext } from '@/lib/superinterfaceContext'
10 |
11 | const currentScript = document.currentScript
12 | const superinterfaceContext = getSuperinterfaceContext({ currentScript })
13 | const root = createRoot(rootElement({ currentScript }))
14 |
15 | root.render(
16 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 |
--------------------------------------------------------------------------------
/packages/javascript/src/components/Providers/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | useSuperinterfaceContext,
3 | useAssistant,
4 | } from '@superinterface/react'
5 | import { Theme } from '@radix-ui/themes'
6 |
7 | export const ThemeProvider = ({
8 | children,
9 | }: {
10 | children: React.ReactNode
11 | }) => {
12 | const superinterfaceContext = useSuperinterfaceContext()
13 |
14 | const { assistant } = useAssistant({
15 | assistantId: superinterfaceContext.variables.assistantId,
16 | })
17 |
18 | if (!assistant) {
19 | return null
20 | }
21 |
22 | return (
23 |
32 | {children}
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/packages/javascript/src/components/Providers/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | QueryClient,
3 | QueryClientProvider,
4 | } from '@tanstack/react-query'
5 | import { useState } from 'react'
6 | import {
7 | SuperinterfaceProvider,
8 | AssistantProvider,
9 | } from '@superinterface/react'
10 | import { ThemeProvider } from './ThemeProvider'
11 | import './styles.css'
12 |
13 | type Args = {
14 | children: React.ReactNode
15 | superinterfaceContext: Record
16 | }
17 |
18 | export const Providers = ({
19 | children,
20 | superinterfaceContext,
21 | }: Args) => {
22 | const [queryClient] = useState(() => (
23 | new QueryClient({
24 | defaultOptions: {
25 | queries: {
26 | // With SSR, we usually want to set some default staleTime
27 | // above 0 to avoid refetching immediately on the client
28 | staleTime: 10000,
29 | },
30 | },
31 | })
32 | ))
33 |
34 | return (
35 |
36 |
37 |
38 |
39 | {children}
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/packages/javascript/src/components/Providers/reset.css:
--------------------------------------------------------------------------------
1 | .superinterface * {
2 | margin: 0;
3 | padding: 0;
4 | font-family: inherit;
5 | font-weight: inherit;
6 | font-size: inherit;
7 | text-decoration: none;
8 | color: inherit;
9 | background-color: transparent;
10 | border: 0;
11 | outline: 0;
12 | text-transform: none;
13 | -webkit-appearance: inherit;
14 | box-sizing: border-box;
15 | /* Safari 12 calc + vw resize fix */
16 | min-height: 0vw;
17 | white-space: normal;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/javascript/src/components/Providers/styles.css:
--------------------------------------------------------------------------------
1 | @import './reset.css';
2 | @import '@radix-ui/themes/styles.css';
3 |
4 | .superinterface {
5 | display: flex;
6 | flex-grow: 1;
7 | max-height: 100dvh;
8 | }
9 |
10 | .superinterface .radix-themes {
11 | --default-font-family: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
12 | --cursor-button: pointer;
13 | --cursor-menu-item: pointer;
14 | display: flex;
15 | flex-grow: 1;
16 | min-height: inherit;
17 | z-index: auto;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/javascript/src/lib/superinterfaceContext/currentScriptSuperinterfaceContext.ts:
--------------------------------------------------------------------------------
1 | export const currentScriptSuperinterfaceContext = ({
2 | currentScript,
3 | }: {
4 | currentScript: HTMLScriptElement
5 | }) => {
6 | const url = new URL(currentScript.src)
7 |
8 | const {
9 | baseUrl,
10 | ...variables
11 | } = Object.fromEntries(url.searchParams.entries())
12 |
13 | return {
14 | ...(baseUrl ? { baseUrl } : {}),
15 | variables,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/javascript/src/lib/superinterfaceContext/index.ts:
--------------------------------------------------------------------------------
1 | import { isValidSuperinterfaceContext } from './isValidSuperinterfaceContext'
2 | import {
3 | currentScriptSuperinterfaceContext as getCurrentScriptSuperinterfaceContext,
4 | } from './currentScriptSuperinterfaceContext'
5 | import { windowSuperinterfaceContext } from './windowSuperinterfaceContext'
6 |
7 | declare const INJECTED_SUPERINTERFACE_CONTEXT: Record | undefined
8 |
9 | export const superinterfaceContext = ({
10 | currentScript,
11 | }: {
12 | currentScript: HTMLOrSVGScriptElement | null
13 | }) => {
14 | if (typeof INJECTED_SUPERINTERFACE_CONTEXT !== 'undefined') {
15 | return INJECTED_SUPERINTERFACE_CONTEXT
16 | }
17 |
18 | if (currentScript instanceof HTMLScriptElement) {
19 | const currentScriptSuperinterfaceContext = getCurrentScriptSuperinterfaceContext({ currentScript })
20 |
21 | if (isValidSuperinterfaceContext({ superinterfaceContext: currentScriptSuperinterfaceContext })) {
22 | return currentScriptSuperinterfaceContext
23 | }
24 | }
25 |
26 | const windowSuperinterface = (window as any).superinterface
27 |
28 | if (windowSuperinterface) {
29 | return windowSuperinterfaceContext({
30 | windowSuperinterface,
31 | })
32 | }
33 |
34 | throw new Error('Superinterface context is not set up. Please read Superinterface integration docs.')
35 | }
36 |
--------------------------------------------------------------------------------
/packages/javascript/src/lib/superinterfaceContext/isValidSuperinterfaceContext.ts:
--------------------------------------------------------------------------------
1 | export const isValidSuperinterfaceContext = ({
2 | superinterfaceContext,
3 | }: {
4 | superinterfaceContext: Record
5 | }) => {
6 | if (superinterfaceContext.baseUrl) {
7 | return true
8 | }
9 |
10 | if (Object.keys(superinterfaceContext.variables).length) {
11 | return true
12 | }
13 |
14 | return false
15 | }
16 |
--------------------------------------------------------------------------------
/packages/javascript/src/lib/superinterfaceContext/windowSuperinterfaceContext.ts:
--------------------------------------------------------------------------------
1 | export const windowSuperinterfaceContext = ({
2 | windowSuperinterface: {
3 | baseUrl,
4 | ...variables
5 | },
6 | }: {
7 | windowSuperinterface: Record
8 | }) => ({
9 | ...(baseUrl ? { baseUrl } : {}),
10 | variables,
11 | })
12 |
--------------------------------------------------------------------------------
/packages/javascript/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "declaration": false,
6 | "noEmit": false,
7 | "paths": {
8 | "@/*": ["./src/*"]
9 | }
10 | },
11 | "include": ["src"],
12 | "exclude": ["node_modules", "dist"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/javascript/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | define: {
5 | 'process.env.NODE_ENV': JSON.stringify('production'),
6 | 'process.env': '{}',
7 | },
8 | entry: [
9 | 'src/index.ts',
10 | 'src/Thread.tsx',
11 | 'src/ThreadAnnotationsComponentSource.tsx',
12 | 'src/ThreadAnnotationsComponentDisabled.tsx',
13 | 'src/ThreadFilesEnabled.tsx',
14 | 'src/ThreadFilesEnabledAnnotationsComponentSource.tsx',
15 | 'src/ThreadFilesEnabledAnnotationsComponentDisabled.tsx',
16 | 'src/AudioThread.tsx',
17 | 'src/AudioThreadWebrtcAudioRuntime.tsx',
18 | 'src/ThreadDialog.tsx',
19 | 'src/ThreadDialogAnnotationsComponentSource.tsx',
20 | 'src/ThreadDialogAnnotationsComponentDisabled.tsx',
21 | 'src/ThreadDialogFilesEnabled.tsx',
22 | 'src/ThreadDialogFilesEnabledAnnotationsComponentSource.tsx',
23 | 'src/ThreadDialogFilesEnabledAnnotationsComponentDisabled.tsx',
24 | 'src/AudioThreadDialog.tsx',
25 | 'src/AudioThreadDialogWebrtcAudioRuntime.tsx',
26 | ],
27 | sourcemap: true,
28 | clean: true,
29 | shims: true,
30 | platform: 'browser',
31 | minify: true,
32 | format: [
33 | 'iife',
34 | 'cjs',
35 | 'esm'
36 | ],
37 | injectStyle: true,
38 | dts: false,
39 | noExternal: [
40 | /(.*)/
41 | ],
42 | })
43 |
--------------------------------------------------------------------------------
/packages/react/src/components/annotations/FilePathAnnotation.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import OpenAI from 'openai'
4 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
5 | import { Link } from '@/contexts/markdown/MarkdownContext/lib/components/Link'
6 |
7 | export const FilePathAnnotation = ({
8 | annotation,
9 | children,
10 | }: {
11 | annotation: OpenAI.Beta.Threads.Messages.FilePathAnnotation
12 | children: React.ReactNode
13 | }) => {
14 | const superinterfaceContext = useSuperinterfaceContext()
15 | const nextSearchParams = new URLSearchParams(superinterfaceContext.variables)
16 |
17 | return (
18 |
23 | {children}
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/components/annotations/SourceAnnotation/FileCitation/Content.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | Card,
4 | Inset,
5 | } from '@radix-ui/themes'
6 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
7 |
8 | export const Content = ({
9 | fileId,
10 | }: {
11 | fileId: string
12 | }) => {
13 | const superinterfaceContext = useSuperinterfaceContext()
14 | const nextSearchParams = new URLSearchParams(superinterfaceContext.variables)
15 |
16 | return (
17 |
22 |
28 |
35 |
38 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/packages/react/src/components/annotations/SourceAnnotation/index.tsx:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { FilePathAnnotation } from '@/components/annotations/FilePathAnnotation'
3 | import { FileCitation } from './FileCitation'
4 |
5 | export const SourceAnnotation = ({
6 | children,
7 | ...rest
8 | }: {
9 | children: React.ReactNode
10 | ['data-annotation']: string
11 | }) => {
12 | const annotation = JSON.parse(rest['data-annotation'] ?? '{}') as OpenAI.Beta.Threads.Messages.Annotation
13 |
14 | if (annotation.type === 'file_citation') {
15 | return (
16 |
19 | )
20 | } else if (annotation.type === 'file_path') {
21 | return (
22 |
25 | {children}
26 |
27 | )
28 | }
29 |
30 | return null
31 | }
32 |
--------------------------------------------------------------------------------
/packages/react/src/components/assistants/AssistantProvider/Code.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { useMarkdownContext } from '@/hooks/markdown/useMarkdownContext'
3 | import { Suggestions } from '@/components/suggestions/Suggestions'
4 | import { useMessageContext } from '@/hooks/messages/useMessageContext'
5 |
6 | export const Code = ({
7 | children,
8 | className,
9 | markdownContext,
10 | }: {
11 | children: string,
12 | className: string,
13 | markdownContext: ReturnType
14 | }) => {
15 | const messageContext = useMessageContext()
16 |
17 | const isAssistantMessage = useMemo(() => (
18 | messageContext.message?.role === 'assistant'
19 | ), [messageContext])
20 |
21 | if (!isAssistantMessage || className !== 'language-suggestions') {
22 | return markdownContext.components.code({ children, className })
23 | }
24 |
25 | return {children}
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/components/assistants/AssistantProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
2 | import { AssistantAvatarContext } from '@/contexts/assistants/AssistantAvatarContext'
3 | import { AssistantNameContext } from '@/contexts/assistants/AssistantNameContext'
4 | import { useAssistant } from '@/hooks/assistants/useAssistant'
5 | import { Avatar } from '@/components/avatars/Avatar'
6 |
7 | type Args = {
8 | children: React.ReactNode
9 | }
10 |
11 | export const AssistantProvider = ({
12 | children,
13 | }: Args) => {
14 | const superinterfaceContext = useSuperinterfaceContext()
15 | const { assistant } = useAssistant({
16 | assistantId: superinterfaceContext.variables.assistantId,
17 | })
18 |
19 | return (
20 |
21 | }
23 | >
24 | {children}
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/src/components/audioRuntimes/TtsAudioRuntimeProvider.tsx:
--------------------------------------------------------------------------------
1 | import { AudioThreadContext } from '@/contexts/threads/AudioThreadContext'
2 | import type { PlayArgs } from '@/types'
3 | import { useTtsAudioRuntime } from '@/hooks/audioRuntimes/useTtsAudioRuntime'
4 |
5 | export const TtsAudioRuntimeProvider = ({
6 | children,
7 | play,
8 | }: {
9 | children: React.ReactNode
10 | play?: (args: PlayArgs) => void
11 | }) => {
12 | const { ttsAudioRuntime } = useTtsAudioRuntime({
13 | play,
14 | })
15 |
16 | return (
17 |
22 | {children}
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react/src/components/audioRuntimes/WebrtcAudioRuntimeProvider.tsx:
--------------------------------------------------------------------------------
1 | import { AudioThreadContext } from '@/contexts/threads/AudioThreadContext'
2 | import { useWebrtcAudioRuntime } from '@/hooks/audioRuntimes/useWebrtcAudioRuntime'
3 |
4 | export const WebrtcAudioRuntimeProvider = ({
5 | children,
6 | }: {
7 | children: React.ReactNode
8 | }) => {
9 | const { webrtcAudioRuntime } = useWebrtcAudioRuntime()
10 |
11 | return (
12 |
17 | {children}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react/src/components/avatars/Avatar.tsx:
--------------------------------------------------------------------------------
1 | import { AvatarType as AvatarTypeEnum } from '@/lib/enums'
2 | import { Avatar as AvatarType, StyleProps } from '@/types'
3 | import { Avatar as RadixAvatar } from '@radix-ui/themes'
4 | import { ImageAvatar } from '@/components/imageAvatars/ImageAvatar'
5 | import { IconAvatar } from '@/components/iconAvatars/IconAvatar'
6 |
7 | export const Avatar = ({
8 | avatar,
9 | size = '1',
10 | className,
11 | style,
12 | }: {
13 | avatar: AvatarType
14 | size?: '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
15 | } & StyleProps) => {
16 | if (avatar) {
17 | if (avatar.type === AvatarTypeEnum.IMAGE && avatar.imageAvatar) {
18 | return (
19 |
25 | )
26 | }
27 |
28 | if (avatar.type === AvatarTypeEnum.ICON && avatar.iconAvatar) {
29 | return (
30 |
36 | )
37 | }
38 | }
39 |
40 | return (
41 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/packages/react/src/components/components/ComponentsProvider.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
3 | import { useComponents } from '@/hooks/components/useComponents'
4 | import { merge } from '@/lib/misc/merge'
5 |
6 | export const ComponentsProvider = ({
7 | children,
8 | ...rest
9 | }: {
10 | children: React.ReactNode
11 | components: Record>
12 | }) => {
13 | const prevComponents = useComponents()
14 |
15 | const value = useMemo(() => (
16 | merge(prevComponents, rest)
17 | ), [rest, prevComponents])
18 |
19 | return (
20 |
23 | {children}
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/components/contents/ImageFileContent.tsx:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
3 | import { Image } from '@/components/images/Image'
4 |
5 | export const ImageFileContent = ({
6 | content,
7 | }: {
8 | content: OpenAI.Beta.Threads.Messages.ImageFileContentBlock
9 | }) => {
10 | const superinterfaceContext = useSuperinterfaceContext()
11 | const nextSearchParams = new URLSearchParams(superinterfaceContext.variables)
12 |
13 | return (
14 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/functions/Function/index.tsx:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { FunctionBase } from '@/components/functions/FunctionBase'
3 | import { title } from './lib/title'
4 |
5 | type Args = {
6 | fn: OpenAI.Beta.Threads.Runs.FunctionToolCall.Function
7 | runStep: OpenAI.Beta.Threads.Runs.RunStep
8 | }
9 |
10 | export const Function = ({
11 | fn,
12 | runStep,
13 | }: Args) => (
14 |
19 | )
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/functions/Function/lib/title.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 |
3 | type Args = {
4 | fn: OpenAI.Beta.Threads.Runs.FunctionToolCall.Function
5 | runStep: OpenAI.Beta.Threads.Runs.RunStep
6 | }
7 |
8 | export const title = ({
9 | fn,
10 | runStep,
11 | }: Args) => {
12 | if (runStep.completed_at) {
13 | return `Finished ${fn.name}`
14 | } else if (runStep.cancelled_at) {
15 | return `Cancelled ${fn.name}`
16 | } else {
17 | return `Calling ${fn.name}`
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/functions/FunctionBase/Content/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { Code, Box } from '@radix-ui/themes'
3 | import OpenAI from 'openai'
4 | import { formattedJsonOrRaw } from './lib/formattedJsonOrRaw'
5 |
6 | type Args = {
7 | fn: OpenAI.Beta.Threads.Runs.FunctionToolCall.Function
8 | }
9 |
10 | export const Content = ({
11 | fn,
12 | }: Args) => {
13 | const args = useMemo(() => (
14 | formattedJsonOrRaw({
15 | value: fn.arguments,
16 | })
17 | ), [fn])
18 |
19 | const output = useMemo(() => (
20 | formattedJsonOrRaw({
21 | value: fn.output,
22 | })
23 | ), [fn])
24 |
25 | return (
26 |
34 | {args &&
35 | {args}
36 | }
37 | {output &&
38 | {output}
39 | }
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/packages/react/src/components/functions/FunctionBase/Content/lib/formattedJsonOrRaw.ts:
--------------------------------------------------------------------------------
1 | export const formattedJsonOrRaw = ({
2 | value,
3 | }: {
4 | value: string | null
5 | }) => {
6 | if (!value) {
7 | return null
8 | }
9 |
10 | try {
11 | return JSON.stringify(JSON.parse(value), null, 2)
12 | } catch (error) {
13 | if (typeof value === 'string') {
14 | return value
15 | } else {
16 | return JSON.stringify(value, null, 2)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/functions/FunctionBase/Icon.tsx:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import {
3 | CircleIcon,
4 | CircleBackslashIcon,
5 | CheckCircledIcon,
6 | } from '@radix-ui/react-icons'
7 |
8 | type Args = {
9 | runStep: OpenAI.Beta.Threads.Runs.RunStep
10 | }
11 |
12 | export const Icon = ({
13 | runStep,
14 | }: Args) => {
15 | if (runStep.completed_at) {
16 | return (
17 |
18 | )
19 | } else if (runStep.cancelled_at || runStep.failed_at || runStep.status === 'expired') {
20 | return (
21 |
22 | )
23 | } else {
24 | return (
25 |
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/src/components/functions/FunctionBase/index.tsx:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import {
3 | Popover,
4 | Flex,
5 | } from '@radix-ui/themes'
6 | import { ToolCallBase } from '@/components/toolCalls/ToolCallBase'
7 | import { ToolCallTitle } from '@/components/toolCalls/ToolCallBase/ToolCallTitle'
8 | import { ToolCallIcon } from '@/components/toolCalls/ToolCallBase/ToolCallIcon'
9 | import { Content } from './Content'
10 |
11 | type Args = {
12 | fn: OpenAI.Beta.Threads.Runs.FunctionToolCall.Function
13 | runStep: OpenAI.Beta.Threads.Runs.RunStep
14 | title: string
15 | }
16 |
17 | export const FunctionBase = ({
18 | fn,
19 | runStep,
20 | title,
21 | }: Args) => (
22 |
23 |
24 |
25 |
26 |
27 |
28 | {title}
29 |
30 |
31 |
32 |
33 |
38 |
39 |
40 |
41 | )
42 |
--------------------------------------------------------------------------------
/packages/react/src/components/iconAvatars/IconAvatar.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { IconAvatar as IconAvatarType, StyleProps } from '@/types'
3 | import { Avatar } from '@radix-ui/themes'
4 | import { iconAvatarComponents } from '@/lib/iconAvatars/iconAvatarComponents'
5 |
6 | export const IconAvatar = ({
7 | iconAvatar,
8 | size,
9 | className,
10 | style,
11 | }: {
12 | iconAvatar: IconAvatarType
13 | size: '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
14 | } & StyleProps) => {
15 | const Component = useMemo(() => (
16 | iconAvatarComponents[iconAvatar.name]
17 | ), [iconAvatar])
18 |
19 | return (
20 |
27 | ) : (
28 | ''
29 | )
30 | )}
31 | />
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react/src/components/imageAvatars/ImageAvatar/index.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar } from '@radix-ui/themes'
2 | import { Size, ImageAvatar as ImageAvatarType, StyleProps } from '@/types'
3 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
4 | import { optimizedSrc } from './lib/optimizedSrc'
5 |
6 | export const ImageAvatar = ({
7 | imageAvatar,
8 | size,
9 | className,
10 | style,
11 | }: {
12 | imageAvatar: ImageAvatarType
13 | size: Size
14 | } & StyleProps) => {
15 | const superinterfaceContext = useSuperinterfaceContext()
16 |
17 | return (
18 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/components/imageAvatars/ImageAvatar/lib/optimizedSrc/host.ts:
--------------------------------------------------------------------------------
1 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
2 |
3 | export const host = ({
4 | superinterfaceContext,
5 | }: {
6 | superinterfaceContext: ReturnType
7 | }) => {
8 | if (!superinterfaceContext.baseUrl) return ''
9 | if (!/^https?:\/\//i.test(superinterfaceContext.baseUrl)) return ''
10 |
11 | return new URL(superinterfaceContext.baseUrl).origin
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react/src/components/imageAvatars/ImageAvatar/lib/optimizedSrc/index.ts:
--------------------------------------------------------------------------------
1 | import { Size, ImageAvatar } from '@/types'
2 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
3 | import { path } from './path'
4 | import { host } from './host'
5 |
6 | export const optimizedSrc = ({
7 | imageAvatar,
8 | size,
9 | superinterfaceContext,
10 | }: {
11 | imageAvatar: ImageAvatar
12 | size: Size
13 | superinterfaceContext: ReturnType
14 | }) => {
15 | if (!imageAvatar.url) return ''
16 | if (imageAvatar.url.endsWith('.svg')) return imageAvatar.url
17 |
18 | return `${host({ superinterfaceContext })}${path({ imageAvatar, size })}`
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/imageAvatars/ImageAvatar/lib/optimizedSrc/path.ts:
--------------------------------------------------------------------------------
1 | import { Size, ImageAvatar } from '@/types'
2 |
3 | const width = ({
4 | size,
5 | }: {
6 | size: Size
7 | }) => {
8 | if (size === '1') {
9 | return 48
10 | } else if (size === '2') {
11 | return 64
12 | } else if (size === '3') {
13 | return 96
14 | } else if (size === '4') {
15 | return 96
16 | } else if (size === '5') {
17 | return 128
18 | } else if (size === '6') {
19 | return 256
20 | } else if (size === '7') {
21 | return 256
22 | } else if (size === '8') {
23 | return 256
24 | } else if (size === '9') {
25 | return 384
26 | }
27 |
28 | return 384
29 | }
30 |
31 | export const path = ({
32 | imageAvatar,
33 | size,
34 | }: {
35 | imageAvatar: ImageAvatar
36 | size: Size
37 | }) => (
38 | `/_next/image?url=${encodeURIComponent(imageAvatar.url)}&w=${width({ size })}&q=95`
39 | )
40 |
--------------------------------------------------------------------------------
/packages/react/src/components/images/Image.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | } from '@radix-ui/themes'
4 |
5 | export const Image = (props: JSX.IntrinsicElements['img']) => (
6 |
9 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/react/src/components/markdown/MarkdownProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { MarkdownContext, type MarkdownContextType } from '@/contexts/markdown/MarkdownContext'
3 | import { useMarkdownContext } from '@/hooks/markdown/useMarkdownContext'
4 | import { merge } from '@/lib/misc/merge'
5 |
6 | export const MarkdownProvider = ({
7 | children,
8 | ...rest
9 | }: {
10 | children: React.ReactNode
11 | } & Partial) => {
12 | const prevMarkdownContext = useMarkdownContext()
13 |
14 | const value = useMemo(() => (
15 | merge(prevMarkdownContext, rest)
16 | ), [rest, prevMarkdownContext])
17 |
18 | return (
19 |
22 | {children}
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react/src/components/media/MediaContainer.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Card,
3 | Inset,
4 | } from '@radix-ui/themes'
5 |
6 | export const MediaContainer = ({
7 | children,
8 | }: {
9 | children: React.ReactNode
10 | }) => (
11 |
14 |
21 | {children}
22 |
23 |
24 | )
25 |
--------------------------------------------------------------------------------
/packages/react/src/components/media/PlayButton.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | PlayButton as VidstackPlayButton,
3 | useMediaState,
4 | } from '@vidstack/react'
5 | import {
6 | IconButton,
7 | } from '@radix-ui/themes'
8 | import {
9 | PlayIcon,
10 | PauseIcon,
11 | } from '@radix-ui/react-icons'
12 |
13 | export const PlayButton = () => {
14 | const isPaused = useMediaState('paused')
15 |
16 | return (
17 |
21 |
22 | {isPaused ? : }
23 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/components/media/Time.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | useState,
3 | useEffect,
4 | useMemo,
5 | } from 'react'
6 | import {
7 | useMediaState,
8 | useMediaRemote,
9 | } from '@vidstack/react'
10 | import {
11 | Slider,
12 | } from '@radix-ui/themes'
13 |
14 | export const Time = () => {
15 | const time = useMediaState('currentTime')
16 | const duration = useMediaState('duration')
17 | const seeking = useMediaState('seeking')
18 | const canSeek = useMediaState('canSeek')
19 | const remote = useMediaRemote()
20 |
21 | const [value, setValue] = useState(0)
22 |
23 | useEffect(() => {
24 | if (seeking) return;
25 | setValue((time / duration) * 100);
26 | }, [time, duration, seeking])
27 |
28 | const step = useMemo(() => (
29 | (1 / duration) * 100
30 | ), [duration])
31 |
32 | return (
33 | {
40 | setValue(value)
41 | remote.seeking((value / 100) * duration)
42 | }}
43 | onValueCommit={([value]) => {
44 | remote.seek((value / 100) * duration)
45 | }}
46 | />
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/packages/react/src/components/media/VolumeButton.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | MuteButton,
3 | useMediaState,
4 | useMediaRemote,
5 | } from '@vidstack/react'
6 | import {
7 | IconButton,
8 | HoverCard,
9 | Slider,
10 | } from '@radix-ui/themes'
11 | import {
12 | SpeakerModerateIcon,
13 | SpeakerOffIcon,
14 | } from '@radix-ui/react-icons'
15 |
16 | export const VolumeButton = () => {
17 | const volume = useMediaState('volume')
18 | const isMuted = useMediaState('muted')
19 | const remote = useMediaRemote()
20 |
21 | return (
22 |
23 |
24 |
28 |
29 | {(isMuted || volume === 0) ? : }
30 |
31 |
32 |
33 |
38 | (
44 | remote.changeVolume(value / 100)
45 | )}
46 | />
47 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/packages/react/src/components/messageGroups/MessageGroup/AssistantAvatar.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import {
4 | Flex,
5 | } from '@radix-ui/themes'
6 | import { AssistantAvatarContext } from '@/contexts/assistants/AssistantAvatarContext'
7 | import type { StyleProps } from '@/types'
8 |
9 | type Args = {
10 | children?: React.ReactNode
11 | } & StyleProps
12 |
13 | const Root = ({
14 | children,
15 | style,
16 | className,
17 | }: Args) => (
18 |
29 | {children}
30 |
31 | )
32 |
33 | export const AssistantAvatar = ({
34 | style,
35 | className,
36 | }: StyleProps) => {
37 | const AssistantAvatarContextValue = useContext(AssistantAvatarContext)
38 |
39 | return (
40 |
44 | {AssistantAvatarContextValue}
45 |
46 | )
47 | }
48 |
49 | AssistantAvatar.Root = Root
50 |
--------------------------------------------------------------------------------
/packages/react/src/components/messageGroups/MessageGroup/Messages.tsx:
--------------------------------------------------------------------------------
1 | import { Flex } from '@radix-ui/themes'
2 | import { MessageGroup } from '@/types'
3 | import { Message } from '@/components/threads/Thread/Message'
4 |
5 | type Args = {
6 | messageGroup: MessageGroup
7 | }
8 |
9 | export const Messages = ({
10 | messageGroup,
11 | }: Args) => (
12 |
15 | {messageGroup.messages.map((message) => (
16 |
20 | ))}
21 |
22 | )
23 |
--------------------------------------------------------------------------------
/packages/react/src/components/messageGroups/MessageGroup/Name.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | Text,
4 | } from '@radix-ui/themes'
5 |
6 | type Args = {
7 | children: React.ReactNode
8 | }
9 |
10 | export const Name = ({
11 | children,
12 | }: Args) => (
13 |
17 |
21 | {children}
22 |
23 |
24 | )
25 |
--------------------------------------------------------------------------------
/packages/react/src/components/messageGroups/MessageGroup/Root.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef } from 'react'
2 | import {
3 | Flex,
4 | Container,
5 | } from '@radix-ui/themes'
6 | import type { StyleProps } from '@/types'
7 |
8 | type Args = {
9 | children: React.ReactNode
10 | } & StyleProps
11 |
12 | export const Root = forwardRef(function Root({
13 | children,
14 | style,
15 | className,
16 | }: Args, ref: React.Ref) {
17 | return (
18 |
25 |
29 | {children}
30 |
31 |
32 | )
33 | })
34 |
--------------------------------------------------------------------------------
/packages/react/src/components/messageGroups/MessageGroup/UserAvatar.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import {
4 | Flex,
5 | } from '@radix-ui/themes'
6 | import { UserAvatarContext } from '@/contexts/users/UserAvatarContext'
7 |
8 | export const UserAvatar = () => {
9 | const UserAvatarContextValue = useContext(UserAvatarContext)
10 |
11 | return (
12 |
21 | {UserAvatarContextValue}
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react/src/components/messages/MessageAttachments.tsx:
--------------------------------------------------------------------------------
1 | import { SerializedMessage } from '@/types'
2 | import {
3 | Flex,
4 | Badge,
5 | } from '@radix-ui/themes'
6 | import {
7 | FileIcon,
8 | } from '@radix-ui/react-icons'
9 |
10 | export const MessageAttachments = ({
11 | message,
12 | }: {
13 | message: SerializedMessage
14 | }) => {
15 | if (!message.attachments?.length) return null
16 |
17 | return (
18 |
22 |
26 |
27 | {message.attachments.length} file{message.attachments.length > 1 ? 's' : ''}
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/packages/react/src/components/messages/MessageContent/ContentPart/index.tsx:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { useComponents } from '@/hooks/components/useComponents'
3 |
4 | const TextContent = ({
5 | content,
6 | }: {
7 | content: OpenAI.Beta.Threads.Messages.TextContentBlock
8 | }) => {
9 | const {
10 | components: {
11 | TextContent,
12 | },
13 | } = useComponents()
14 |
15 | return (
16 |
19 | )
20 | }
21 |
22 | const ImageFileContent = ({
23 | content,
24 | }: {
25 | content: OpenAI.Beta.Threads.Messages.ImageFileContentBlock
26 | }) => {
27 | const {
28 | components: {
29 | ImageFileContent,
30 | },
31 | } = useComponents()
32 |
33 | return (
34 |
37 | )
38 | }
39 |
40 | export const ContentPart = ({
41 | content,
42 | }: {
43 | content: OpenAI.Beta.Threads.Messages.MessageContent
44 | }) => {
45 | if (content.type === 'text') {
46 | return (
47 |
50 | )
51 | }
52 |
53 | if (content.type === 'image_file') {
54 | return (
55 |
58 | )
59 | }
60 |
61 | return null
62 | }
63 |
--------------------------------------------------------------------------------
/packages/react/src/components/messages/MessageContent/index.tsx:
--------------------------------------------------------------------------------
1 | import { SerializedMessage } from '@/types'
2 | import { ContentPart } from './ContentPart'
3 |
4 | export const MessageContent = ({
5 | message,
6 | }: {
7 | message: SerializedMessage
8 | }) => (
9 | <>
10 | {message.content.map((content, index) => (
11 |
15 | ))}
16 | >
17 | )
18 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunStep/ToolCalls/ToolCall/CodeInterpreter.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import OpenAI from 'openai'
3 | import { useContext } from 'react'
4 | import type { SerializedRunStep } from '@/types'
5 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
6 |
7 | export const CodeInterpreter = ({
8 | codeInterpreter,
9 | runStep,
10 | }: {
11 | codeInterpreter: OpenAI.Beta.Threads.Runs.CodeInterpreterToolCall.CodeInterpreter
12 | runStep: SerializedRunStep
13 | }) => {
14 | const componentsContext = useContext(ComponentsContext)
15 | const Component = componentsContext.components.CodeInterpreterToolCall
16 |
17 | return (
18 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunStep/ToolCalls/ToolCall/Fallback.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import type { SerializedRunStep, ToolCall } from '@/types'
4 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
5 |
6 | export const Fallback = ({
7 | toolCall,
8 | runStep,
9 | }: {
10 | toolCall: ToolCall
11 | runStep: SerializedRunStep
12 | }) => {
13 | const componentsContext = useContext(ComponentsContext)
14 | const Component = componentsContext.components.FallbackToolCall
15 |
16 | return (
17 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunStep/ToolCalls/ToolCall/FileSearch.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import type { SerializedRunStep, ToolCall } from '@/types'
4 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
5 |
6 | export const FileSearch = ({
7 | toolCall,
8 | runStep,
9 | }: {
10 | toolCall: ToolCall
11 | runStep: SerializedRunStep
12 | }) => {
13 | const componentsContext = useContext(ComponentsContext)
14 | const Component = componentsContext.components.FileSearchToolCall
15 |
16 | return (
17 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunStep/ToolCalls/ToolCall/Fn.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import OpenAI from 'openai'
4 | import type { SerializedRunStep } from '@/types'
5 | import { FunctionComponentsContext } from '@/contexts/functions/FunctionComponentsContext'
6 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
7 |
8 | type Args = {
9 | fn: OpenAI.Beta.Threads.Runs.FunctionToolCall.Function
10 | runStep: SerializedRunStep
11 | }
12 |
13 | export const Fn = ({
14 | fn,
15 | runStep,
16 | }: Args) => {
17 | const functionComponentsContext = useContext(FunctionComponentsContext)
18 | const componentsContext = useContext(ComponentsContext)
19 | const Component = functionComponentsContext[fn.name] || componentsContext.components.Function
20 |
21 | return (
22 | // @ts-ignore-next-line
23 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunStep/ToolCalls/ToolCall/index.tsx:
--------------------------------------------------------------------------------
1 | import { Fn } from './Fn'
2 | import { CodeInterpreter } from './CodeInterpreter'
3 | import { FileSearch } from './FileSearch'
4 | import { Fallback } from './Fallback'
5 | import type { SerializedRunStep, ToolCall as ToolCallType } from '@/types'
6 |
7 | type Args = {
8 | toolCall: ToolCallType
9 | runStep: SerializedRunStep
10 | }
11 |
12 | export const ToolCall = ({
13 | toolCall,
14 | runStep,
15 | }: Args) => {
16 | if (toolCall.type === 'function') {
17 | return (
18 |
22 | )
23 | }
24 |
25 | if (toolCall.type === 'code_interpreter') {
26 | return (
27 |
31 | )
32 | }
33 |
34 | if (toolCall.type === 'file_search') {
35 | return (
36 |
40 | )
41 | }
42 |
43 | return (
44 |
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunStep/index.tsx:
--------------------------------------------------------------------------------
1 | import type { SerializedRunStep, StyleProps } from '@/types'
2 | import { ToolCalls } from './ToolCalls'
3 |
4 | type Args = {
5 | runStep: SerializedRunStep
6 | } & StyleProps
7 |
8 | export const RunStep = ({
9 | runStep,
10 | className,
11 | style,
12 | }: Args) => {
13 | if (runStep.step_details.type === 'tool_calls') {
14 | return (
15 |
21 | )
22 | }
23 |
24 | return null
25 | }
26 |
27 | RunStep.ToolCalls = ToolCalls
28 |
--------------------------------------------------------------------------------
/packages/react/src/components/runSteps/RunSteps/index.tsx:
--------------------------------------------------------------------------------
1 | import { Flex } from '@radix-ui/themes'
2 | import { useContext } from 'react'
3 | import type { SerializedRunStep } from '@/types'
4 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
5 |
6 | type Args = {
7 | runSteps: SerializedRunStep[]
8 | }
9 |
10 | export const RunSteps = ({
11 | runSteps,
12 | }: Args) => {
13 | const componentsContext = useContext(ComponentsContext)
14 | const Component = componentsContext.components.RunStep
15 |
16 | return (
17 |
20 | {runSteps.map((runStep) => (
21 |
25 | ))}
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/src/components/skeletons/StartingContentSkeleton/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Skeleton,
3 | } from '@radix-ui/themes'
4 |
5 | export const StartingContentSkeleton = () => (
6 |
16 | )
17 |
--------------------------------------------------------------------------------
/packages/react/src/components/skeletons/StartingSkeleton/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import { Box } from '@radix-ui/themes'
4 | import { StartingContentSkeleton } from '@/components/skeletons/StartingContentSkeleton'
5 | import { MessageGroup } from '@/components/messageGroups/MessageGroup'
6 | import { AssistantNameContext } from '@/contexts/assistants/AssistantNameContext'
7 |
8 | type Args = {
9 | children?: React.ReactNode
10 | }
11 |
12 | export const StartingSkeleton = ({
13 | children,
14 | }: Args) => (
15 |
16 | {children}
17 |
18 |
19 |
20 | )
21 |
22 | const Root = ({
23 | children,
24 | }: Args) => {
25 | const assistantNameContext = useContext(AssistantNameContext)
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 | {assistantNameContext}
34 |
35 |
36 | {children}
37 |
38 |
39 | )
40 | }
41 |
42 | const Content = StartingContentSkeleton
43 |
44 | StartingSkeleton.Root = Root
45 | StartingSkeleton.Content = Content
46 |
--------------------------------------------------------------------------------
/packages/react/src/components/suggestions/Suggestions/Content.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { isEmpty } from 'radash'
3 | import { onlyText } from 'react-children-utilities'
4 | import { useIsMutatingMessage } from '@/hooks/messages/useIsMutatingMessage'
5 | import {
6 | Flex,
7 | } from '@radix-ui/themes'
8 | import type { StyleProps } from '@/types'
9 | import { Item } from './Item'
10 |
11 | export const Content = ({
12 | children,
13 | className,
14 | style,
15 | }: {
16 | children: React.ReactNode
17 | } & StyleProps) => {
18 | const isMutatingMessage = useIsMutatingMessage()
19 |
20 | const suggestions = useMemo(() => (
21 | onlyText(children).split(/\r?\n/).filter((c) => !isEmpty(c)).map((c) => c.trim())
22 | ), [children])
23 |
24 | if (isEmpty(suggestions)) return null
25 |
26 | return (
27 |
34 | {suggestions.map((suggestion) => (
35 |
40 | ))}
41 |
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react/src/components/suggestions/Suggestions/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { useLatestMessage } from '@/hooks/messages/useLatestMessage'
3 | import type { StyleProps } from '@/types'
4 | import { Content } from './Content'
5 | import { Item } from './Item'
6 |
7 | export const Suggestions = ({
8 | children,
9 | className,
10 | style,
11 | }: {
12 | children: React.ReactNode
13 | } & StyleProps) => {
14 | const latestMessageProps = useLatestMessage()
15 |
16 | const isDisabled = useMemo(() => (
17 | // @ts-ignore-next-line
18 | latestMessageProps.latestMessage?.metadata?.isBlocking
19 | ), [latestMessageProps])
20 |
21 | if (latestMessageProps.isLoading) return null
22 | if (isDisabled) return null
23 |
24 | return (
25 |
29 | {children}
30 |
31 | )
32 | }
33 |
34 | Suggestions.Item = Item
35 |
--------------------------------------------------------------------------------
/packages/react/src/components/textareas/TextareaBase/index.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef } from 'react'
2 | import TextareaAutosize from 'react-textarea-autosize'
3 | import type { StyleProps } from '@/types'
4 |
5 | type Props = React.ComponentProps & StyleProps
6 |
7 | export const TextareaBase = forwardRef(function TextareaBase({
8 | style,
9 | className,
10 | ...rest
11 | }: Props, ref) {
12 | return (
13 | <>
14 |
18 |
19 |
35 | >
36 | )
37 | })
38 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/AudioThread/Form/MicIcon.tsx:
--------------------------------------------------------------------------------
1 | import { SVGProps } from 'react'
2 |
3 | export const MicIcon = (props: SVGProps) => (
4 |
19 | )
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/AudioThread/Status/StatusMessages.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | Text,
4 | } from '@radix-ui/themes'
5 | import type { StyleProps } from '@/types'
6 |
7 | const html = ({ texts }: { texts: string[] }) => `
8 | .status-messages-texts:after {
9 | content: '${texts[0]}';
10 | animation: texts ${texts.length * 5}s linear infinite;
11 | }
12 |
13 | @keyframes texts {
14 | ${texts.map((_, i) => `
15 | ${i * 100 / texts.length}% {
16 | content: "${texts[i]}";
17 | }
18 | `).join('')}
19 | }`
20 |
21 | export const StatusMessages = ({
22 | texts,
23 | className,
24 | style,
25 | }: {
26 | texts: string[]
27 | } & StyleProps) => (
28 |
34 |
41 |
42 |
47 |
48 | )
49 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/AudioThread/Status/index.tsx:
--------------------------------------------------------------------------------
1 | import type { StyleProps } from '@/types'
2 | import { useStatus } from '@/hooks/audioThreads/useStatus'
3 | import { StatusMessages } from './StatusMessages'
4 |
5 | export const Status = (props: StyleProps) => {
6 | const { status } = useStatus()
7 |
8 | if (status === 'recording') {
9 | return (
10 |
19 | )
20 | }
21 |
22 | if (['recorderPaused', 'idle', 'playerPaused'].includes(status)) {
23 | return (
24 |
30 | )
31 | }
32 |
33 | if (status === 'playing') {
34 | return (
35 |
41 | )
42 | }
43 |
44 | return (
45 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/AudioThread/index.tsx:
--------------------------------------------------------------------------------
1 | import { Root, Args as RootArgs } from './Root'
2 | import { Visualization } from './Visualization'
3 | import { Status } from './Status'
4 | import { Form } from './Form'
5 |
6 | type Args = Omit
7 |
8 | export const AudioThread = (props: Args) => (
9 |
10 |
11 |
12 |
13 |
14 | )
15 |
16 | AudioThread.Root = Root
17 | AudioThread.Visualization = Visualization
18 | AudioThread.Status = Status
19 | AudioThread.Form = Form
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/AudioThreadDialog/index.tsx:
--------------------------------------------------------------------------------
1 | import { AudioThread } from '@/components/threads/AudioThread'
2 | import { Root } from '@/components/threads/ThreadDialog/Root'
3 | import { Trigger } from '@/components/threads/ThreadDialog/Trigger'
4 | import { Content } from '@/components/threads/ThreadDialog/Content'
5 | import type { StyleProps } from '@/types'
6 |
7 | export const AudioThreadDialog = (props: StyleProps) => (
8 |
9 |
10 |
11 |
12 |
13 |
14 | )
15 |
16 | AudioThreadDialog.Root = Root
17 | AudioThreadDialog.Trigger = Trigger
18 | AudioThreadDialog.Content = Content
19 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Message/Attachments/index.tsx:
--------------------------------------------------------------------------------
1 | import { SerializedMessage } from '@/types'
2 | import { useComponents } from '@/hooks/components/useComponents'
3 |
4 | export const Attachments = ({
5 | message,
6 | }: {
7 | message: SerializedMessage
8 | }) => {
9 | const {
10 | components: {
11 | MessageAttachments,
12 | },
13 | } = useComponents()
14 |
15 | return (
16 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Message/Provider.tsx:
--------------------------------------------------------------------------------
1 | import { MessageContext } from '@/contexts/messages/MessageContext'
2 |
3 | export const Provider = MessageContext.Provider
4 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/MessageForm/Field/Files/index.tsx:
--------------------------------------------------------------------------------
1 | import { Preview } from './Preview'
2 | import { Control } from './Control'
3 |
4 | export const Files = {
5 | Preview,
6 | Control,
7 | }
8 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/MessageForm/Root/lib/formOptions.ts:
--------------------------------------------------------------------------------
1 | import { z } from 'zod'
2 | import { zodResolver } from '@hookform/resolvers/zod'
3 |
4 | const schema = z.object({
5 | content: z.string().min(1),
6 | })
7 |
8 | export const formOptions = {
9 | resolver: zodResolver(schema),
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/MessageForm/index.tsx:
--------------------------------------------------------------------------------
1 | import type { StyleProps } from '@/types'
2 | import { Submit } from './Submit'
3 | import { Root } from './Root'
4 | import { Field } from './Field'
5 |
6 | export const MessageForm = (props: StyleProps) => (
7 |
8 |
9 |
10 |
11 |
12 |
13 | )
14 |
15 | MessageForm.Root = Root
16 | MessageForm.Field = Field
17 | MessageForm.Submit = Submit
18 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Messages/NextPageSkeleton.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useMessages } from '@/hooks/messages/useMessages'
3 | import { MessagesSkeleton } from '@/components/skeletons/MessagesSkeleton'
4 |
5 | export const NextPageSkeleton = () => {
6 | const {
7 | hasNextPage,
8 | } = useMessages()
9 |
10 | if (!hasNextPage) {
11 | return null
12 | }
13 |
14 | return (
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Messages/Root/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { Flex } from '@radix-ui/themes'
4 | import { useInfiniteScroll } from '@/hooks/misc/useInfiniteScroll'
5 | import { useMessages } from '@/hooks/messages/useMessages'
6 | import type { StyleProps } from '@/types'
7 |
8 | export const Root = ({
9 | children,
10 | style,
11 | className,
12 | }: {
13 | children: React.ReactNode
14 | } & StyleProps) => {
15 | const {
16 | isFetchingNextPage,
17 | hasNextPage,
18 | fetchNextPage,
19 | } = useMessages()
20 |
21 | const { containerRef, loaderRef } = useInfiniteScroll({
22 | isFetchingNextPage,
23 | hasNextPage,
24 | fetchNextPage,
25 | })
26 |
27 | return (
28 |
38 | {children}
39 |
40 | {hasNextPage && (
41 |
44 | )}
45 |
46 |
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Messages/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | } from '@radix-ui/themes'
4 | import { Message } from '@/components/threads/Thread/Message'
5 | import { StyleProps } from '@/types'
6 | import { Content } from './Content'
7 | import { Progress } from './Progress'
8 | import { Root } from './Root'
9 | import { NextPageSkeleton } from './NextPageSkeleton'
10 |
11 | type Args = {
12 | children?: React.ReactNode
13 | } & StyleProps
14 |
15 | export const Messages = ({
16 | children,
17 | className,
18 | style,
19 | }: Args) => (
20 |
24 |
28 |
29 |
30 |
31 | {children}
32 |
33 |
34 |
35 |
36 |
37 |
42 |
43 | )
44 |
45 | Messages.Root = Root
46 | Messages.Message = Message
47 | Messages.NextPageSkeleton = NextPageSkeleton
48 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Provider/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { SuperinterfaceProvider } from '@/components/core/SuperinterfaceProvider'
4 | import type { Args as SuperinterfaceProviderArgs } from '@/components/core/SuperinterfaceProvider'
5 |
6 | export type Args = SuperinterfaceProviderArgs
7 | export const Provider = SuperinterfaceProvider
8 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/Root/index.tsx:
--------------------------------------------------------------------------------
1 | import { Flex } from '@radix-ui/themes'
2 | import {
3 | Provider,
4 | Args as ThreadProviderArgs,
5 | } from '@/components/threads/Thread/Provider'
6 | import type { StyleProps } from '@/types'
7 | import { ToastsProvider } from '@/components/toasts/ToastsProvider'
8 |
9 | export type Args = ThreadProviderArgs & StyleProps
10 |
11 | export const Root = ({
12 | children,
13 | style,
14 | className,
15 | ...rest
16 | }: Args) => (
17 |
18 |
19 |
25 | {children}
26 |
27 |
28 |
29 | )
30 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/Thread/index.tsx:
--------------------------------------------------------------------------------
1 | import { Messages } from '@/components/threads/Thread/Messages'
2 | import { MessageForm } from '@/components/threads/Thread/MessageForm'
3 | import { Root, Args as RootArgs } from '@/components/threads/Thread/Root'
4 |
5 | type Args = Omit
6 |
7 | export const Thread = (props: Args) => (
8 |
9 |
10 |
11 |
12 | )
13 |
14 | Thread.Root = Root
15 | Thread.Messages = Messages
16 | Thread.MessageForm = MessageForm
17 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/ThreadDialog/Close/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Cross1Icon,
3 | } from '@radix-ui/react-icons'
4 | import {
5 | IconButton,
6 | Flex,
7 | } from '@radix-ui/themes'
8 | import { useThreadDialogContext } from '@/hooks/threads/useThreadDialogContext'
9 |
10 | export const Close = () => {
11 | const { setIsOpen, isOpen } = useThreadDialogContext()
12 |
13 | return (
14 | setIsOpen((prev) => !prev)}
20 | direction="column"
21 | flexShrink="0"
22 | justify="end"
23 | align="end"
24 | position="absolute"
25 | top="24px"
26 | right='24px'
27 | style={{
28 | zIndex: 9999999999,
29 | }}
30 | >
31 |
35 |
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/ThreadDialog/Provider/index.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { ThreadDialogContext } from '@/contexts/threads/ThreadDialogContext'
3 | import { useThreadDialogContext } from '@/hooks/threads/useThreadDialogContext'
4 |
5 | type Args = {
6 | children: React.ReactNode
7 | }
8 |
9 | export const Provider = ({
10 | children,
11 | }: Args) => {
12 | const threadDialogContext = useThreadDialogContext()
13 | const [isOpen, setIsOpen] = useState(threadDialogContext.isOpen)
14 |
15 | return (
16 |
22 | {children}
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/ThreadDialog/Root/index.tsx:
--------------------------------------------------------------------------------
1 | import { Provider } from '@/components/threads/ThreadDialog/Provider'
2 |
3 | type Args = {
4 | children: React.ReactNode
5 | }
6 |
7 | export const Root = ({
8 | children,
9 | }: Args) => (
10 |
11 | {children}
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/ThreadDialog/Trigger/Button.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IconButton,
3 | } from '@radix-ui/themes'
4 | import {
5 | ChatBubbleIcon,
6 | } from '@radix-ui/react-icons'
7 | import type { StyleProps } from '@/types'
8 |
9 | export const Button = (props: StyleProps) => (
10 |
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/ThreadDialog/Trigger/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | } from '@radix-ui/themes'
4 | import { useThreadDialogContext } from '@/hooks/threads/useThreadDialogContext'
5 | import { Button } from './Button'
6 | import type { StyleProps } from '@/types'
7 |
8 | type Args = {
9 | children: React.ReactNode
10 | } & StyleProps
11 |
12 | const Root = ({
13 | children,
14 | style,
15 | className,
16 | }: Args) => {
17 | const { setIsOpen, isOpen } = useThreadDialogContext()
18 |
19 | return (
20 | setIsOpen((prev) => !prev)}
26 | direction="column"
27 | flexShrink="0"
28 | justify="end"
29 | align="end"
30 | position="fixed"
31 | bottom="24px"
32 | right="24px"
33 | className={className}
34 | style={{
35 | zIndex: 9999999999,
36 | ...(style ?? {}),
37 | }}
38 | >
39 | {children}
40 |
41 | )
42 | }
43 |
44 | export const Trigger = (args: Omit) => (
45 |
46 |
47 |
48 | )
49 |
50 | Trigger.Root = Root
51 | Trigger.Button = Button
52 |
--------------------------------------------------------------------------------
/packages/react/src/components/threads/ThreadDialog/index.tsx:
--------------------------------------------------------------------------------
1 | import { Root } from '@/components/threads/ThreadDialog/Root'
2 | import { Trigger } from '@/components/threads/ThreadDialog/Trigger'
3 | import { Content } from '@/components/threads/ThreadDialog/Content'
4 |
5 | export const ThreadDialog = () => (
6 |
7 |
8 |
9 |
10 | )
11 |
12 | ThreadDialog.Root = Root
13 | ThreadDialog.Trigger = Trigger
14 | ThreadDialog.Content = Content
15 |
--------------------------------------------------------------------------------
/packages/react/src/components/toasts/ToastsProvider/CustomToast.tsx:
--------------------------------------------------------------------------------
1 | import * as Toast from '@radix-ui/react-toast'
2 | import {
3 | Card,
4 | Text,
5 | Flex,
6 | } from '@radix-ui/themes'
7 | import {
8 | CheckCircledIcon,
9 | CrossCircledIcon,
10 | } from '@radix-ui/react-icons'
11 | import { Toast as ToastType } from '@/types'
12 |
13 | export const CustomToast = ({
14 | toast,
15 | }: {
16 | toast: ToastType,
17 | }) => (
18 |
19 |
20 |
21 |
22 |
27 | {toast.type === 'success' ? (
28 |
31 | ) : (
32 |
35 | )}
36 |
37 |
41 | {toast.message}
42 |
43 |
44 |
45 |
46 |
47 | )
48 |
--------------------------------------------------------------------------------
/packages/react/src/components/toolCalls/FallbackToolCall.tsx:
--------------------------------------------------------------------------------
1 | import { ToolCallBase } from '@/components/toolCalls/ToolCallBase'
2 | import { ToolCallTitle } from '@/components/toolCalls/ToolCallBase/ToolCallTitle'
3 | import { ToolCallIcon } from '@/components/toolCalls/ToolCallBase/ToolCallIcon'
4 | import type { SerializedRunStep, ToolCall } from '@/types'
5 |
6 | export const FallbackToolCall = ({
7 | runStep,
8 | toolCall,
9 | }: {
10 | toolCall: ToolCall
11 | runStep: SerializedRunStep
12 | }) => (
13 |
14 |
15 |
16 | Using tool: {toolCall.type}
17 |
18 |
19 | )
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/toolCalls/FileSearchToolCall.tsx:
--------------------------------------------------------------------------------
1 | import { ToolCallBase } from '@/components/toolCalls/ToolCallBase'
2 | import { ToolCallTitle } from '@/components/toolCalls/ToolCallBase/ToolCallTitle'
3 | import { ToolCallIcon } from '@/components/toolCalls/ToolCallBase/ToolCallIcon'
4 | import type { SerializedRunStep, ToolCall } from '@/types'
5 |
6 | export const FileSearchToolCall = ({
7 | runStep,
8 | toolCall,
9 | }: {
10 | toolCall: ToolCall
11 | runStep: SerializedRunStep
12 | }) => (
13 |
14 |
15 |
16 | Searching files
17 |
18 |
19 | )
20 |
--------------------------------------------------------------------------------
/packages/react/src/components/toolCalls/StartingToolCalls.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Popover,
3 | Text,
4 | } from '@radix-ui/themes'
5 | import {
6 | CircleIcon,
7 | } from '@radix-ui/react-icons'
8 | import { ToolCallBase } from '@/components/toolCalls/ToolCallBase'
9 | import { ToolCallTitle } from '@/components/toolCalls/ToolCallBase/ToolCallTitle'
10 |
11 | export const StartingToolCalls = () => (
12 |
13 |
14 |
15 |
16 | Starting actions
17 |
18 |
19 |
24 |
25 | Starting some actions
26 |
27 |
28 |
29 | )
30 |
--------------------------------------------------------------------------------
/packages/react/src/components/toolCalls/ToolCallBase/ToolCallIcon.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | CircleIcon,
3 | CircleBackslashIcon,
4 | CheckCircledIcon,
5 | } from '@radix-ui/react-icons'
6 | import type { SerializedRunStep } from '@/types'
7 |
8 | type Args = {
9 | runStep: SerializedRunStep
10 | }
11 |
12 | export const ToolCallIcon = ({
13 | runStep,
14 | }: Args) => {
15 | if (runStep.completed_at) {
16 | return (
17 |
18 | )
19 | } else if (runStep.cancelled_at || runStep.failed_at || runStep.status === 'expired') {
20 | return (
21 |
22 | )
23 | } else {
24 | return (
25 |
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/src/components/toolCalls/ToolCallBase/ToolCallTitle.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Text,
3 | } from '@radix-ui/themes'
4 |
5 | type Args = {
6 | children: React.ReactNode
7 | }
8 |
9 | export const ToolCallTitle = ({
10 | children,
11 | }: Args) => (
12 |
15 | {children}
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/react/src/components/toolCalls/ToolCallBase/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Flex,
3 | Button,
4 | } from '@radix-ui/themes'
5 |
6 | export const ToolCallBase = ({
7 | children,
8 | }: {
9 | children: React.ReactNode
10 | }) => (
11 |
15 |
25 |
26 | )
27 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/assistants/AssistantAvatarContext/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 | import {
4 | Avatar,
5 | } from '@radix-ui/themes'
6 | import {
7 | LightningBoltIcon,
8 | } from '@radix-ui/react-icons'
9 |
10 | export const AssistantAvatarContext = createContext(
11 | }
13 | size="1"
14 | />
15 | )
16 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/assistants/AssistantNameContext/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 |
4 | export const AssistantNameContext = createContext('Assistant')
5 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/core/SuperinterfaceContext/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 | import { baseUrl } from '@/lib/superinterfaceCloud/baseUrl'
5 | import { cookieOptions } from '@/lib/threadIdStorage/cookieOptions'
6 | import { localStorageOptions } from '@/lib/threadIdStorage/localStorageOptions'
7 | import { isIframe } from '@/lib/iframes/isIframe'
8 | import type { ThreadStorageOptions } from '@/types'
9 |
10 | export const SuperinterfaceContext = createContext<{
11 | baseUrl: string | null
12 | variables: Record
13 | defaultOptions: {
14 | queries: Record
15 | mutations: Record
16 | }
17 | threadIdStorageOptions: ThreadStorageOptions | null
18 | createMessageAbortControllerRef: React.MutableRefObject
19 | }>({
20 | baseUrl,
21 | variables: {},
22 | defaultOptions: {
23 | queries: {},
24 | mutations: {},
25 | },
26 | threadIdStorageOptions: isIframe() ? localStorageOptions : cookieOptions,
27 | createMessageAbortControllerRef: { current: null },
28 | })
29 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/functions/FunctionComponentsContext/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 |
4 | type FunctionComponents = {
5 | [key: string]: React.ReactNode
6 | }
7 |
8 | export const FunctionComponentsContext = createContext({} as FunctionComponents)
9 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 | import { components } from './lib/components'
4 | import { getRemarkPlugins } from './lib/getRemarkPlugins'
5 |
6 | export type MarkdownContextType = {
7 | components: typeof components,
8 | getRemarkPlugins: typeof getRemarkPlugins,
9 | }
10 |
11 | export const MarkdownContext = createContext({
12 | components,
13 | getRemarkPlugins,
14 | })
15 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Annotation/AnnotationBase.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IconButton,
3 | Popover,
4 | Flex,
5 | Text,
6 | } from '@radix-ui/themes'
7 |
8 | export const AnnotationBase = ({
9 | icon,
10 | content,
11 | }: {
12 | icon: React.ReactNode,
13 | content: string
14 | }) => (
15 |
16 |
17 |
22 | {icon}
23 |
24 |
25 |
28 |
31 |
35 | {content}
36 |
37 |
38 |
39 |
40 | )
41 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Annotation/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | QuoteIcon,
3 | } from '@radix-ui/react-icons'
4 | import OpenAI from 'openai'
5 | import { FilePathAnnotation } from '@/components/annotations/FilePathAnnotation'
6 | import { AnnotationBase } from './AnnotationBase'
7 |
8 | export const Annotation = ({
9 | children,
10 | ...rest
11 | }: {
12 | children: React.ReactNode
13 | ['data-annotation']: string
14 | }) => {
15 | const annotation = JSON.parse(rest['data-annotation'] ?? '{}') as OpenAI.Beta.Threads.Messages.Annotation
16 |
17 | if (annotation.type === 'file_citation') {
18 | return (
19 | }
21 | content="File cited."
22 | />
23 | )
24 | } else if (annotation.type === 'file_path') {
25 | return (
26 |
29 | {children}
30 |
31 | )
32 | }
33 |
34 | return null
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Code.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Code as RadixCode,
3 | } from '@radix-ui/themes'
4 | import { Suggestions } from '@/components/suggestions/Suggestions'
5 |
6 | export const Code = ({
7 | children,
8 | className,
9 | }: JSX.IntrinsicElements['code']) => {
10 | if (className === 'language-suggestions') {
11 | return {children}
12 | }
13 |
14 | return (
15 |
20 | {children}
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Img/Video/FullscreenButton.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | FullscreenButton as VidstackFullscreenButton,
3 | useMediaState,
4 | } from '@vidstack/react'
5 | import {
6 | IconButton,
7 | } from '@radix-ui/themes'
8 | import {
9 | EnterFullScreenIcon,
10 | ExitFullScreenIcon,
11 | } from '@radix-ui/react-icons'
12 |
13 | export const FullscreenButton = () => {
14 | const isFullscreen = useMediaState('fullscreen')
15 |
16 | return (
17 |
21 |
22 | {isFullscreen ? : }
23 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Img/index.tsx:
--------------------------------------------------------------------------------
1 | import { Image } from '@/components/images/Image'
2 | import { isVideoSrc } from './lib/isVideoSrc'
3 | import { isAudioSrc } from './lib/isAudioSrc'
4 | import { Video } from './Video'
5 | import { Audio } from './Audio'
6 |
7 | export const Img = (props: JSX.IntrinsicElements['img']) => {
8 | if (!props.src) {
9 | return (
10 |
13 | )
14 | } else if (isVideoSrc({ src: props.src })) {
15 | return (
16 |
19 | )
20 | } else if (isAudioSrc({ src: props.src })) {
21 | return (
22 |
25 | )
26 | } else {
27 | return (
28 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Img/lib/isAudioSrc.ts:
--------------------------------------------------------------------------------
1 | export const isAudioSrc = ({
2 | src,
3 | }: {
4 | src: string
5 | }) => (
6 | src.endsWith('.mp3') || src.endsWith('.wav')
7 | )
8 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Img/lib/isVideoSrc.ts:
--------------------------------------------------------------------------------
1 | // list is from https://www.npmjs.com/package/video-extensions
2 | const videoExtensions = [
3 | '3g2',
4 | '3gp',
5 | 'aaf',
6 | 'asf',
7 | 'avchd',
8 | 'avi',
9 | 'drc',
10 | 'flv',
11 | 'm2v',
12 | 'm3u8',
13 | 'm4p',
14 | 'm4v',
15 | 'mkv',
16 | 'mng',
17 | 'mov',
18 | 'mp2',
19 | 'mp4',
20 | 'mpe',
21 | 'mpeg',
22 | 'mpg',
23 | 'mpv',
24 | 'mxf',
25 | 'nsv',
26 | 'ogg',
27 | 'ogv',
28 | 'qt',
29 | 'rm',
30 | 'rmvb',
31 | 'roq',
32 | 'svi',
33 | 'vob',
34 | 'webm',
35 | 'wmv',
36 | 'yuv',
37 | ]
38 |
39 | export const isVideoSrc = ({
40 | src,
41 | }: {
42 | src: string
43 | }) => (
44 | videoExtensions.includes(src.split('.').pop() || '')
45 | )
46 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Link.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Link as RadixLink,
3 | } from '@radix-ui/themes'
4 |
5 | type Args = JSX.IntrinsicElements['a']
6 |
7 | export const Link = ({
8 | children,
9 | href,
10 | download,
11 | target = '_blank',
12 | }: Args) => (
13 |
18 | {children}
19 |
20 | )
21 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/ListItem.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | } from '@radix-ui/themes'
4 |
5 | export const ListItem = ({ children }: JSX.IntrinsicElements['li']) => (
6 |
9 |
14 | {children}
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/OrderedList.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | } from '@radix-ui/themes'
4 |
5 | export const OrderedList = ({ children }: JSX.IntrinsicElements['ul']) => (
6 |
10 |
15 | {children}
16 |
17 |
18 | )
19 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Paragraph.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Text,
4 | } from '@radix-ui/themes'
5 |
6 | export const Paragraph = ({ children }: JSX.IntrinsicElements['p']) => (
7 |
10 |
17 | {children}
18 |
19 |
20 | )
21 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Pre.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | } from '@radix-ui/themes'
4 |
5 | export const Pre = ({ children }: JSX.IntrinsicElements['pre']) => (
6 |
12 | {children}
13 |
14 | )
15 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/Strong.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Strong as RadixStrong,
3 | } from '@radix-ui/themes'
4 |
5 | export const Strong = ({ children }: JSX.IntrinsicElements['strong']) => (
6 |
7 | {children}
8 |
9 | )
10 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/components/UnorderedList.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | } from '@radix-ui/themes'
4 |
5 | export const UnorderedList = ({ children }: JSX.IntrinsicElements['ul']) => (
6 |
10 |
17 |
18 | )
19 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/markdown/MarkdownContext/lib/getRemarkPlugins.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import remarkGfm from 'remark-gfm'
3 | import { remarkAnnotation } from '@/lib/remark/remarkAnnotation'
4 | import { remarkPureLiteralPlugin } from '@/lib/remark/remarkPureLiteralPlugin'
5 |
6 | export const getRemarkPlugins = ({
7 | content,
8 | }: {
9 | content: OpenAI.Beta.Threads.Messages.TextContentBlock
10 | }) => [
11 | remarkPureLiteralPlugin,
12 | remarkAnnotation({ content }),
13 | remarkGfm,
14 | ]
15 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/messages/MessageContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 | import { SerializedMessage } from '@/types'
4 |
5 | export const MessageContext = createContext<{
6 | message: SerializedMessage | null
7 | }>({
8 | message: null,
9 | })
10 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/messages/MessageFormContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import OpenAI from 'openai'
3 | import { createContext, Dispatch, SetStateAction } from 'react'
4 |
5 | export const MessageFormContext = createContext({
6 | content: '',
7 | isDisabled: false,
8 | isLoading: false,
9 | files: [] as OpenAI.Files.FileObject[],
10 | setFiles: (() => {}) as Dispatch>,
11 | isFileLoading: false,
12 | })
13 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/threads/AudioThreadContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { createContext } from 'react'
4 | import type { AudioRuntime } from '@/types'
5 |
6 | export const AudioThreadContext = createContext<{
7 | audioRuntime: AudioRuntime | null
8 | }>({
9 | audioRuntime: null,
10 | })
11 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/threads/ThreadDialogContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext, Dispatch, SetStateAction } from 'react'
3 |
4 | export const ThreadDialogContext = createContext<{
5 | isOpen: boolean
6 | setIsOpen: Dispatch>
7 | }>({
8 | isOpen: false,
9 | setIsOpen: () => {},
10 | })
11 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/toasts/ToastsContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 | import { Toast } from '@/types'
4 |
5 | export const ToastsContext = createContext<{
6 | toasts: Toast[]
7 | addToast: (toast: Toast) => void
8 | }>({
9 | toasts: [],
10 | addToast: () => {},
11 | })
12 |
--------------------------------------------------------------------------------
/packages/react/src/contexts/users/UserAvatarContext/index.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { createContext } from 'react'
3 | import {
4 | Avatar,
5 | } from '@radix-ui/themes'
6 | import {
7 | PersonIcon,
8 | } from '@radix-ui/react-icons'
9 |
10 | export const UserAvatarContext = createContext(
11 | }
13 | size="1"
14 | />
15 | )
16 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/assistants/useAssistant/index.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | useQuery,
4 | } from '@tanstack/react-query'
5 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
6 | import { queryOptions } from './lib/queryOptions'
7 |
8 | type Args = {
9 | assistantId: string
10 | }
11 |
12 | export const useAssistant = ({
13 | assistantId,
14 | }: Args) => {
15 | const superinterfaceContext = useSuperinterfaceContext()
16 | const props = useQuery(queryOptions({
17 | assistantId,
18 | superinterfaceContext,
19 | }))
20 |
21 | return useMemo(() => ({
22 | ...props,
23 | assistant: props.data ? props.data.assistant : null,
24 | }), [props])
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/assistants/useAssistant/lib/queryOptions/index.ts:
--------------------------------------------------------------------------------
1 | import { queryOptions as tanstackQueryOptions } from '@tanstack/react-query'
2 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
3 | import { queryFn } from './queryFn'
4 |
5 | type Args = {
6 | assistantId: string
7 | superinterfaceContext: ReturnType
8 | }
9 |
10 | export const queryOptions = ({
11 | assistantId,
12 | superinterfaceContext,
13 | }: Args) => (
14 | tanstackQueryOptions({
15 | queryKey: ['assistants', { assistantId }],
16 | queryFn: queryFn({ superinterfaceContext }),
17 | })
18 | )
19 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/assistants/useAssistant/lib/queryOptions/queryFn.ts:
--------------------------------------------------------------------------------
1 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
2 |
3 | type QueryFunctionArgs = {
4 | queryKey: Readonly<['assistants', { assistantId: string }]>
5 | }
6 |
7 | export const queryFn = ({
8 | superinterfaceContext,
9 | }: {
10 | superinterfaceContext: ReturnType
11 | }) => async ({
12 | queryKey,
13 | }: QueryFunctionArgs) => {
14 | const [_key, { assistantId }] = queryKey
15 |
16 | const params = new URLSearchParams({
17 | publicApiKey: superinterfaceContext.variables.publicApiKey,
18 | })
19 |
20 | return fetch(`${superinterfaceContext.baseUrl}/assistants/${assistantId}?${params}`)
21 | .then(async (response) => {
22 | if (response.status !== 200) {
23 | try {
24 | const errorResponse = await response.json() as { error: string }
25 | throw new Error(errorResponse.error)
26 | } catch (error) {
27 | throw new Error('Failed to fetch')
28 | }
29 | }
30 |
31 | return response.json() as Promise
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/audioRuntimes/useTtsAudioRuntime/blobToData.ts:
--------------------------------------------------------------------------------
1 | export const blobToData = (blob: Blob) => (
2 | new Promise((resolve) => {
3 | const reader = new FileReader()
4 | reader.onloadend = () => resolve(reader.result)
5 | reader.readAsDataURL(blob)
6 | })
7 | )
8 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/audioThreads/useMessageAudio/lib/input.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { isEmpty } from 'radash'
3 | import { SerializedMessage } from '@/types'
4 |
5 | type Args = {
6 | message: SerializedMessage
7 | }
8 |
9 | export const input = ({
10 | message,
11 | }: Args) => {
12 | const textContents = (message.content as OpenAI.Beta.Threads.Messages.TextContentBlock[]).filter((c: OpenAI.Beta.Threads.Messages.TextContentBlock) => (
13 | c.type === 'text'
14 | ))
15 |
16 | const result = textContents.map((c: OpenAI.Beta.Threads.Messages.TextContentBlock) => (
17 | c.text.value
18 | )).join(' ')
19 |
20 | if (isEmpty(result)) return null
21 |
22 | return result
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/audioThreads/useMessageAudio/lib/isHtmlAudioSupported.ts:
--------------------------------------------------------------------------------
1 | import { detect } from 'detect-browser'
2 |
3 | const unsupportedNames = [
4 | 'safari',
5 | 'ios',
6 | ]
7 |
8 | export const isHtmlAudioSupported = !unsupportedNames.includes(detect()?.name || '')
9 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/audioThreads/useStatus/index.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { useAudioThreadContext } from '@/hooks/threads/useAudioThreadContext'
3 | import { statusMessages } from './lib/statusMessages'
4 |
5 | export const useStatus = () => {
6 | const { audioRuntime } = useAudioThreadContext()
7 |
8 | const status = useMemo((): keyof typeof statusMessages => {
9 | if (audioRuntime.user.rawStatus === 'recording') return 'recording'
10 | if (audioRuntime.user.isPending) return 'creatingMessage'
11 | if (audioRuntime.assistant.paused || !audioRuntime.assistant.isAudioPlayed) return 'playerPaused'
12 | if (audioRuntime.assistant.playing || audioRuntime.assistant.isPending) return 'playing'
13 | if (!audioRuntime.assistant.isAudioPlayed && !audioRuntime.assistant.isReady) return 'loading'
14 | if (audioRuntime.user.rawStatus === 'idle') return 'idle'
15 | if (audioRuntime.user.rawStatus === 'paused') return 'recorderPaused'
16 |
17 | return 'loading'
18 | }, [
19 | audioRuntime,
20 | ])
21 |
22 | return {
23 | status,
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/audioThreads/useStatus/lib/statusMessages.ts:
--------------------------------------------------------------------------------
1 | export const statusMessages = {
2 | recording: 'Recording your message...',
3 | creatingMessage: 'Sending your message...',
4 | runActive: 'Thinking...',
5 | playing: 'Assistant is speaking...',
6 | loading: 'Loading...',
7 | idle: 'Click the button below to record your answer.',
8 | recorderPaused: 'Click the button below to resume recording.',
9 | playerPaused: 'Click the button below to resume audio.',
10 | acquiringMedia: 'Getting your microphone ready...',
11 | other: '',
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/components/useComponents.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { ComponentsContext } from '@/contexts/components/ComponentsContext'
3 |
4 | export const useComponents = () => (
5 | useContext(ComponentsContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/core/useSuperinterfaceContext/index.ts:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useContext } from 'react'
3 | import { SuperinterfaceContext } from '@/contexts/core/SuperinterfaceContext'
4 |
5 | export const useSuperinterfaceContext = () => (
6 | useContext(SuperinterfaceContext)
7 | )
8 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/files/useCreateFile/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | useMutation,
3 | useQueryClient,
4 | } from '@tanstack/react-query'
5 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
6 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
7 | import { mutationOptions } from './lib/mutationOptions'
8 |
9 | export const useCreateFile = ({
10 | onError = () => {},
11 | }: {
12 | onError?: (error: unknown) => void
13 | } = {
14 | onError: () => {},
15 | }) => {
16 | const queryClient = useQueryClient()
17 | const superinterfaceContext = useSuperinterfaceContext()
18 | const threadContext = useThreadContext()
19 |
20 | const props = useMutation(mutationOptions({
21 | queryClient,
22 | threadContext,
23 | superinterfaceContext,
24 | onError,
25 | }))
26 |
27 | return {
28 | ...props,
29 | createFile: (...args: any[]) => {
30 | const [firstArg, ...restArgs] = args
31 | return props.mutateAsync({
32 | ...threadContext.variables,
33 | ...firstArg,
34 | }, ...restArgs)
35 | },
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/files/useCreateFile/lib/mutationOptions/index.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import {
3 | useQueryClient,
4 | } from '@tanstack/react-query'
5 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
6 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
7 | import { mutationFn } from './mutationFn'
8 |
9 | export const mutationOptions = ({
10 | queryClient,
11 | threadContext,
12 | superinterfaceContext,
13 | onError,
14 | }: {
15 | queryClient: ReturnType,
16 | threadContext: ReturnType,
17 | superinterfaceContext: ReturnType,
18 | onError: (error: unknown) => void
19 | }) => {
20 | const mutationKey = ['createFile', threadContext.variables]
21 |
22 | return {
23 | mutationFn: mutationFn({
24 | queryClient,
25 | superinterfaceContext,
26 | threadContext,
27 | }),
28 | onError,
29 | ...threadContext.defaultOptions.mutations,
30 | ...queryClient.getMutationDefaults(mutationKey),
31 | mutationKey,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/files/useCreateFile/lib/mutationOptions/mutationFn/body/formData.ts:
--------------------------------------------------------------------------------
1 | export const formData = (variables: any) => {
2 | const data = new FormData()
3 |
4 | for (const key in variables) {
5 | data.append(key, variables[key])
6 | }
7 |
8 | return data
9 | }
10 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/files/useCreateFile/lib/mutationOptions/mutationFn/body/index.ts:
--------------------------------------------------------------------------------
1 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
2 | import { formData } from './formData'
3 |
4 | export const body = ({
5 | variables,
6 | superinterfaceContext,
7 | }: {
8 | variables: {
9 | [key: string]: any
10 | }
11 | superinterfaceContext: ReturnType
12 | }) => {
13 | if (variables.threadId) return formData(variables)
14 | if (!variables.assistantId) return formData(variables)
15 | if (!superinterfaceContext.threadIdStorageOptions?.get) return formData(variables)
16 |
17 | const threadId = superinterfaceContext.threadIdStorageOptions.get({ assistantId: variables.assistantId })
18 | if (!threadId) return formData(variables)
19 |
20 | return formData({
21 | ...variables,
22 | threadId,
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/files/useCreateFile/lib/mutationOptions/mutationFn/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | useQueryClient,
3 | } from '@tanstack/react-query'
4 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
5 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
6 | import { body } from './body'
7 |
8 | export const mutationFn = ({
9 | superinterfaceContext,
10 | queryClient,
11 | threadContext,
12 | }: {
13 | superinterfaceContext: ReturnType
14 | queryClient: ReturnType
15 | threadContext: ReturnType
16 | }) => async (variables: {
17 | content: string
18 | [key: string]: any
19 | }) => {
20 | const response = await fetch(`${superinterfaceContext.baseUrl}/files`, {
21 | method: 'POST',
22 | body: body({
23 | variables,
24 | superinterfaceContext,
25 | }),
26 | })
27 |
28 | const result = await response.json()
29 | return result
30 | }
31 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/markdown/useMarkdownContext/index.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { MarkdownContext } from '@/contexts/markdown/MarkdownContext'
3 |
4 | export const useMarkdownContext = () => (
5 | useContext(MarkdownContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messageGroups/useMessageGroups/index.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { messageGroups as getMessageGroups } from './lib/messageGroups'
3 | import { SerializedMessage } from '@/types'
4 |
5 | type Args = {
6 | messages: SerializedMessage[]
7 | }
8 |
9 | export const useMessageGroups = ({
10 | messages,
11 | }: Args) => (
12 | useMemo(
13 | () => ({
14 | messageGroups: getMessageGroups({ messages }),
15 | }),
16 | [messages]
17 | )
18 | )
19 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messageGroups/useMessageGroups/lib/messageGroups/index.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import { last } from 'radash'
3 | import { SerializedMessage, MessageGroup } from '@/types'
4 | import { order } from '@/lib/messages/order'
5 | import { newGroup } from './newGroup'
6 |
7 | type Args = {
8 | messages: SerializedMessage[]
9 | }
10 |
11 | export const messageGroups = ({
12 | messages,
13 | }: Args) =>
14 | _.reduce(
15 | order({ messages }),
16 | (groups: MessageGroup[], message: SerializedMessage) => {
17 | const group = last(groups)
18 |
19 | if (!group) return newGroup({ groups, message })
20 |
21 | if (group.role !== message.role) {
22 | return newGroup({
23 | groups,
24 | message,
25 | })
26 | }
27 |
28 | return [
29 | ..._.dropRight(groups),
30 | {
31 | ...group,
32 | messages: [...group.messages, message],
33 | },
34 | ]
35 | },
36 | []
37 | )
38 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messageGroups/useMessageGroups/lib/messageGroups/newGroup/index.ts:
--------------------------------------------------------------------------------
1 | import { SerializedMessage, MessageGroup } from '@/types'
2 | import { newGroupItem } from './newGroupItem'
3 |
4 | type Args = {
5 | groups: MessageGroup[]
6 | message: SerializedMessage
7 | }
8 |
9 | export const newGroup = ({ groups, message }: Args) => [
10 | ...groups,
11 | newGroupItem({
12 | message,
13 | }),
14 | ]
15 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messageGroups/useMessageGroups/lib/messageGroups/newGroup/newGroupItem.ts:
--------------------------------------------------------------------------------
1 | import { SerializedMessage } from '@/types'
2 |
3 | type Args = {
4 | message: SerializedMessage
5 | }
6 |
7 | export const newGroupItem = ({ message }: Args) => ({
8 | id: message.id,
9 | role: message.role,
10 | createdAt: message.created_at,
11 | messages: [message],
12 | })
13 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/index.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import {
3 | useMutation,
4 | useQueryClient,
5 | } from '@tanstack/react-query'
6 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
7 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
8 | import { mutationOptions } from './lib/mutationOptions'
9 | import type { UseCreateMessageVariables } from '@/types'
10 |
11 | export const useCreateMessage = ({
12 | onError = () => {},
13 | }: {
14 | onError?: (error: unknown) => void
15 | } = {
16 | onError: () => {},
17 | }) => {
18 | const queryClient = useQueryClient()
19 | const superinterfaceContext = useSuperinterfaceContext()
20 | const threadContext = useThreadContext()
21 |
22 | const props = useMutation(mutationOptions({
23 | queryClient,
24 | threadContext,
25 | superinterfaceContext,
26 | onError,
27 | }))
28 |
29 | const createMessage = useCallback((variables: UseCreateMessageVariables) => (
30 | props.mutateAsync({
31 | ...threadContext.variables,
32 | ...variables,
33 | })
34 | ), [props.mutateAsync, threadContext.variables])
35 |
36 | return {
37 | ...props,
38 | createMessage,
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/index.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import {
3 | useQueryClient,
4 | } from '@tanstack/react-query'
5 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
6 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
7 | import { onMutate } from './onMutate'
8 | import { mutationFn } from './mutationFn'
9 |
10 | export const mutationOptions = ({
11 | queryClient,
12 | threadContext,
13 | superinterfaceContext,
14 | onError,
15 | }: {
16 | queryClient: ReturnType,
17 | threadContext: ReturnType,
18 | superinterfaceContext: ReturnType,
19 | onError: (error: unknown) => void
20 | }) => {
21 | const mutationKey = ['createMessage', threadContext.variables]
22 |
23 | return {
24 | mutationFn: mutationFn({
25 | queryClient,
26 | superinterfaceContext,
27 | threadContext,
28 | }),
29 | onMutate: onMutate({ queryClient }),
30 | onError,
31 | ...threadContext.defaultOptions.mutations,
32 | ...queryClient.getMutationDefaults(mutationKey),
33 | mutationKey,
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/body.ts:
--------------------------------------------------------------------------------
1 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
2 |
3 | export const body = ({
4 | variables,
5 | superinterfaceContext,
6 | }: {
7 | variables: {
8 | [key: string]: any
9 | }
10 | superinterfaceContext: ReturnType
11 | }) => {
12 | if (variables.threadId) return variables
13 | if (!variables.assistantId) return variables
14 | if (!superinterfaceContext.threadIdStorageOptions?.get) return variables
15 |
16 | const threadId = superinterfaceContext.threadIdStorageOptions.get({ assistantId: variables.assistantId })
17 | if (!threadId) return variables
18 |
19 | return {
20 | ...variables,
21 | threadId,
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/extendMessage.ts:
--------------------------------------------------------------------------------
1 | import { last } from 'radash'
2 | import { SerializedMessage } from '@/types'
3 | import { isOptimistic } from '@/lib/optimistic/isOptimistic'
4 |
5 | export const extendMessage = ({
6 | message,
7 | messages,
8 | }: {
9 | message: SerializedMessage
10 | messages: SerializedMessage[]
11 | }) => {
12 | const prevRunMessages = messages.filter((m: SerializedMessage) => (
13 | m.run_id === message.run_id
14 | ))
15 |
16 | const prevOptimitisticRunMessages = prevRunMessages.filter((m: SerializedMessage) => (
17 | isOptimistic({ id: m.id })
18 | ))
19 |
20 | const runSteps = last(prevOptimitisticRunMessages)?.runSteps ?? last(prevRunMessages)?.runSteps ?? []
21 |
22 | return {
23 | ...message,
24 | runSteps,
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/index.ts:
--------------------------------------------------------------------------------
1 | import { threadCreated } from './threadCreated'
2 | import { threadMessageCreated } from './threadMessageCreated'
3 | import { threadMessageDelta } from './threadMessageDelta'
4 | import { threadMessageCompleted } from './threadMessageCompleted'
5 | import { threadRunCreated } from './threadRunCreated'
6 | import { threadRunFailed } from './threadRunFailed'
7 | import { threadRunStepCreated } from './threadRunStepCreated'
8 | import { threadRunStepDelta } from './threadRunStepDelta'
9 | import { threadRunStepCompleted } from './threadRunStepCompleted'
10 | import { threadRunRequiresAction } from './threadRunRequiresAction'
11 |
12 | export const handlers = {
13 | 'thread.created': threadCreated,
14 | 'thread.message.created': threadMessageCreated,
15 | 'thread.message.delta': threadMessageDelta,
16 | 'thread.message.completed': threadMessageCompleted,
17 | 'thread.run.created': threadRunCreated,
18 | 'thread.run.failed': threadRunFailed,
19 | 'thread.run.step.created': threadRunStepCreated,
20 | 'thread.run.step.delta': threadRunStepDelta,
21 | 'thread.run.step.completed': threadRunStepCompleted,
22 | 'thread.run.requires_action': threadRunRequiresAction,
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/threadCreated.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
3 |
4 | export const threadCreated = ({
5 | value,
6 | superinterfaceContext,
7 | }: {
8 | value: OpenAI.Beta.Assistants.AssistantStreamEvent.ThreadCreated
9 | superinterfaceContext: ReturnType
10 | }) => {
11 | if (!superinterfaceContext.threadIdStorageOptions?.set) return
12 | // @ts-ignore-next-line
13 | if (!value.data.metadata?.assistantId) return
14 | // @ts-ignore-next-line
15 | if (!value.data.metadata?.threadId) return
16 |
17 | superinterfaceContext.threadIdStorageOptions.set({
18 | // @ts-ignore-next-line
19 | assistantId: value.data.metadata.assistantId,
20 | // @ts-ignore-next-line
21 | threadId: value.data.metadata.threadId,
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/threadMessageCompleted.ts:
--------------------------------------------------------------------------------
1 | import { replace } from 'radash'
2 | import { ThreadMessageCompletedEvent } from '@/types'
3 | import { extendMessage } from './extendMessage'
4 | import {
5 | QueryClient,
6 | } from '@tanstack/react-query'
7 | import { MessagesQueryKey } from '@/types'
8 |
9 | export const threadMessageCompleted = ({
10 | value,
11 | queryClient,
12 | messagesQueryKey,
13 | }: {
14 | messagesQueryKey: MessagesQueryKey
15 | value: ThreadMessageCompletedEvent
16 | queryClient: QueryClient
17 | }) => (
18 | queryClient.setQueryData(
19 | messagesQueryKey,
20 | (prevData: any) => {
21 | if (!prevData) {
22 | return {
23 | pageParams: [],
24 | pages: [
25 | {
26 | data: [],
27 | hasNextPage: false,
28 | lastId: null,
29 | },
30 | ],
31 | }
32 | }
33 |
34 | const [latestPage, ...pagesRest] = prevData.pages
35 |
36 | return {
37 | ...prevData,
38 | pages: [
39 | {
40 | ...latestPage,
41 | data: replace(latestPage.data, extendMessage({ message: value.data, messages: latestPage.data }), (m) => m.id === value.data.id),
42 | },
43 | ...pagesRest,
44 | ],
45 | }
46 | }
47 | )
48 | )
49 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/threadRunFailed.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient } from '@tanstack/react-query'
2 | import OpenAI from 'openai'
3 | import { MessagesQueryKey } from '@/types'
4 |
5 | export const threadRunFailed = ({
6 | value,
7 | queryClient,
8 | messagesQueryKey,
9 | }: {
10 | value: OpenAI.Beta.Assistants.AssistantStreamEvent.ThreadRunCreated
11 | messagesQueryKey: MessagesQueryKey
12 | queryClient: QueryClient
13 | }) => {
14 | throw new Error('Failed to send your message, try again. If you are the owner, check the logs.')
15 | }
16 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/threadRunStepCompleted.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient } from '@tanstack/react-query'
2 | import OpenAI from 'openai'
3 | import { SerializedRunStep, SerializedMessage, MessagesQueryKey } from '@/types'
4 | import { replace } from 'radash'
5 |
6 | export const threadRunStepCompleted = ({
7 | value,
8 | queryClient,
9 | messagesQueryKey,
10 | }: {
11 | value: OpenAI.Beta.Assistants.AssistantStreamEvent.ThreadRunStepCompleted
12 | messagesQueryKey: MessagesQueryKey
13 | queryClient: QueryClient
14 | }) => (
15 | queryClient.setQueryData(
16 | messagesQueryKey,
17 | (prevData: any) => {
18 | if (!prevData) return prevData
19 |
20 | const [latestPage, ...pagesRest] = prevData.pages
21 |
22 | return {
23 | ...prevData,
24 | pages: [
25 | {
26 | ...latestPage,
27 | data: latestPage.data.map((m: SerializedMessage) => {
28 | if (m.run_id === value.data.run_id) {
29 | return {
30 | ...m,
31 | runSteps: replace(m.runSteps, value.data, (rs: SerializedRunStep) => rs.id === value.data.id),
32 | }
33 | }
34 |
35 | return m
36 | }),
37 | },
38 | ...pagesRest,
39 | ],
40 | }
41 | }
42 | )
43 | )
44 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/handlers/threadRunStepCreated.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient } from '@tanstack/react-query'
2 | import OpenAI from 'openai'
3 | import { SerializedMessage, MessagesQueryKey } from '@/types'
4 |
5 | export const threadRunStepCreated = ({
6 | value,
7 | queryClient,
8 | messagesQueryKey,
9 | }: {
10 | value: OpenAI.Beta.Assistants.AssistantStreamEvent.ThreadRunStepCreated
11 | messagesQueryKey: MessagesQueryKey
12 | queryClient: QueryClient
13 | }) => (
14 | queryClient.setQueryData(
15 | messagesQueryKey,
16 | (prevData: any) => {
17 | if (!prevData) return prevData
18 |
19 | const [latestPage, ...pagesRest] = prevData.pages
20 |
21 | return {
22 | ...prevData,
23 | pages: [
24 | {
25 | ...latestPage,
26 | data: latestPage.data.map((m: SerializedMessage) => {
27 | if (m.run_id === value.data.run_id) {
28 | return {
29 | ...m,
30 | runSteps: [
31 | value.data,
32 | ...m.runSteps,
33 | ],
34 | }
35 | }
36 |
37 | return m
38 | }),
39 | },
40 | ...pagesRest,
41 | ],
42 | }
43 | }
44 | )
45 | )
46 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/mutationFn/handleResponse/index.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import {
3 | useQueryClient,
4 | } from '@tanstack/react-query'
5 | import { MessagesQueryKey } from '@/types'
6 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
7 | import { handlers } from './handlers'
8 |
9 | export const handleResponse = ({
10 | value,
11 | messagesQueryKey,
12 | queryClient,
13 | superinterfaceContext,
14 | }: {
15 | value: {
16 | value: OpenAI.Beta.Assistants.AssistantStreamEvent
17 | }
18 | messagesQueryKey: MessagesQueryKey
19 | queryClient: ReturnType
20 | superinterfaceContext: ReturnType
21 | }) => {
22 | // @ts-ignore-next-line
23 | const handler = handlers[value.value.event]
24 |
25 | if (!handler) {
26 | return console.log('Missing handler', { value })
27 | }
28 |
29 | return handler({
30 | value: value.value,
31 | queryClient,
32 | messagesQueryKey,
33 | superinterfaceContext,
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useCreateMessage/lib/mutationOptions/onMutate/index.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { omit } from 'radash'
3 | import { QueryClient } from '@tanstack/react-query'
4 | import { data } from './data'
5 |
6 | type Variables = {
7 | content: string
8 | attachments: OpenAI.Beta.Threads.Messages.Message['attachments'] | undefined
9 | [key: string]: any
10 | }
11 |
12 | export const onMutate = ({
13 | queryClient,
14 | }: {
15 | queryClient: QueryClient,
16 | }) => async (
17 | newMessage: Variables,
18 | ) => {
19 | const queryKey = ['messages', omit(newMessage, ['content', 'attachments'])]
20 | await queryClient.cancelQueries({ queryKey })
21 |
22 | const prevMessages = queryClient.getQueryData(queryKey)
23 |
24 | queryClient.setQueryData(
25 | queryKey,
26 | data({ newMessage })
27 | )
28 |
29 | return {
30 | prevMessages,
31 | newMessage,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useIsMutatingMessage/index.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { useIsMutating } from '@tanstack/react-query'
3 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
4 |
5 | export const useIsMutatingMessage = () => {
6 | const threadContext = useThreadContext()
7 |
8 | const mutatingMessagesCount = useIsMutating({
9 | mutationKey: ['createMessage', threadContext.variables],
10 | })
11 |
12 | const isMutatingMessage = useMemo(() => (
13 | mutatingMessagesCount > 0
14 | ), [mutatingMessagesCount])
15 |
16 | return isMutatingMessage
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useLatestMessage/index.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import { useMessages } from '@/hooks/messages/useMessages'
3 |
4 | export const useLatestMessage = () => {
5 | const props = useMessages()
6 |
7 | return useMemo(() => ({
8 | ...props,
9 | latestMessage: props.messages[0] || null,
10 | }), [props])
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useMessageContext/index.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { MessageContext } from '@/contexts/messages/MessageContext'
3 |
4 | export const useMessageContext = () => (
5 | useContext(MessageContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useMessageFormContext/index.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { MessageFormContext } from '@/contexts/messages/MessageFormContext'
3 |
4 | export const useMessageFormContext = () => (
5 | useContext(MessageFormContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/messages/useMessages/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react'
2 | import {
3 | InfiniteData,
4 | useInfiniteQuery,
5 | useQueryClient,
6 | } from '@tanstack/react-query'
7 | import { SerializedMessage, MessagesPage } from '@/types'
8 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
9 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
10 | import { messagesQueryOptions } from '@/lib/messages/messagesQueryOptions'
11 |
12 | const messages = ({
13 | props,
14 | }: {
15 | props: {
16 | data: InfiniteData | undefined
17 | },
18 | }) => {
19 | if (!props.data) return []
20 |
21 | return props.data.pages.reduce((acc, page) => (
22 | acc.concat(page.data)
23 | ), [])
24 | }
25 |
26 |
27 | export const useMessages = () => {
28 | const queryClient = useQueryClient()
29 | const threadContext = useThreadContext()
30 | const superinterfaceContext = useSuperinterfaceContext()
31 |
32 | const props = useInfiniteQuery(messagesQueryOptions({
33 | queryClient,
34 | threadContext,
35 | superinterfaceContext,
36 | }))
37 |
38 | return useMemo(() => ({
39 | ...props,
40 | // @ts-ignore-next-line
41 | messages: messages({ props }),
42 | }), [props])
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/misc/useInfiniteScroll/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react'
2 | import { useInView } from 'react-intersection-observer'
3 | import { useThrottledEffect } from '@/hooks/misc/useThrottledEffect'
4 |
5 | type Args = {
6 | isFetchingNextPage: boolean
7 | hasNextPage: boolean
8 | fetchNextPage: () => void
9 | }
10 |
11 | export const useInfiniteScroll = ({
12 | isFetchingNextPage,
13 | hasNextPage,
14 | fetchNextPage,
15 | }: Args) => {
16 | const containerRef = useRef(null)
17 |
18 | const { ref: loaderRef, inView } = useInView({
19 | root: containerRef.current,
20 | rootMargin: '0px',
21 | threshold: 0.1,
22 | })
23 |
24 | useThrottledEffect(
25 | () => {
26 | if (isFetchingNextPage) return
27 | if (!inView) return
28 | if (!hasNextPage) return
29 |
30 | fetchNextPage()
31 | },
32 | 500,
33 | [inView, isFetchingNextPage, hasNextPage, fetchNextPage]
34 | )
35 |
36 | return {
37 | containerRef,
38 | loaderRef,
39 | inView,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/misc/useInterval.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react';
2 |
3 | export const useInterval = (callback: Function, delay?: number | null) => {
4 | const savedCallback = useRef(() => {})
5 |
6 | useEffect(() => {
7 | savedCallback.current = callback
8 | })
9 |
10 | useEffect(() => {
11 | if (delay !== null) {
12 | const interval = setInterval(() => savedCallback.current(), delay || 0)
13 | return () => clearInterval(interval)
14 | }
15 |
16 | return undefined
17 | }, [delay])
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/misc/usePermission/util.ts:
--------------------------------------------------------------------------------
1 | // From: https://github.com/streamich/react-use/blob/ade8d3905f544305515d010737b4ae604cc51024/src/misc/util.ts
2 | export const noop = () => {};
3 |
4 | export function on(
5 | obj: T | null,
6 | ...args: Parameters | [string, Function | null, ...any]
7 | ): void {
8 | if (obj && obj.addEventListener) {
9 | obj.addEventListener(...(args as Parameters));
10 | }
11 | }
12 |
13 | export function off(
14 | obj: T | null,
15 | ...args: Parameters | [string, Function | null, ...any]
16 | ): void {
17 | if (obj && obj.removeEventListener) {
18 | obj.removeEventListener(...(args as Parameters));
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/misc/usePrevious.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | export const usePrevious = (state: T): T | undefined => {
4 | const ref = useRef()
5 |
6 | useEffect(() => {
7 | ref.current = state
8 | })
9 |
10 | return ref.current
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/misc/useThrottledEffect/index.tsx:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import { useRef, useEffect, useCallback } from 'react'
3 |
4 | export const useThrottledEffect = (cb: Function, delay: number, additionalDeps: any[]) => {
5 | const cbRef = useRef(cb)
6 |
7 | const throttledCb = useCallback(
8 | _.throttle((...args: any[]) => cbRef.current(...args), delay, {
9 | leading: true,
10 | trailing: true,
11 | }),
12 | [delay]
13 | )
14 | useEffect(() => {
15 | cbRef.current = cb
16 | })
17 | // set additionalDeps to execute effect, when other values change (not only on delay change)
18 | useEffect(throttledCb, [throttledCb, ...additionalDeps])
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/threads/useAudioThreadContext/index.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { AudioThreadContext } from '@/contexts/threads/AudioThreadContext'
3 |
4 | export const useAudioThreadContext = () => (
5 | useContext(AudioThreadContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/threads/useThreadContext/index.ts:
--------------------------------------------------------------------------------
1 | export { useSuperinterfaceContext as useThreadContext } from '@/hooks/core/useSuperinterfaceContext'
2 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/threads/useThreadDialogContext/index.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { ThreadDialogContext } from '@/contexts/threads/ThreadDialogContext'
3 |
4 | export const useThreadDialogContext = () => (
5 | useContext(ThreadDialogContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/hooks/toasts/useToasts/index.ts:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { ToastsContext } from '@/contexts/toasts/ToastsContext'
3 |
4 | export const useToasts = () => (
5 | useContext(ToastsContext)
6 | )
7 |
--------------------------------------------------------------------------------
/packages/react/src/lib/ai/index.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 |
3 | export const defaultClient = new OpenAI({
4 | apiKey: process.env.OPENAI_API_KEY!,
5 | })
6 |
--------------------------------------------------------------------------------
/packages/react/src/lib/enums/index.ts:
--------------------------------------------------------------------------------
1 | export enum IconAvatarName {
2 | BACKPACK = 'BACKPACK',
3 | ROCKET = 'ROCKET',
4 | MAGIC_WAND = 'MAGIC_WAND',
5 | CUBE = 'CUBE',
6 | TARGET = 'TARGET',
7 | DISC = 'DISC',
8 | GLOBE = 'GLOBE',
9 | STAR = 'STAR',
10 | LIGHTNING_BOLT = 'LIGHTNING_BOLT',
11 | FACE = 'FACE',
12 | PERSON = 'PERSON',
13 | HEART = 'HEART',
14 | }
15 |
16 | export enum AvatarType {
17 | ICON = 'ICON',
18 | IMAGE = 'IMAGE',
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react/src/lib/errors/createMessageDefaultOnError.ts:
--------------------------------------------------------------------------------
1 | import {
2 | QueryClient,
3 | } from '@tanstack/react-query'
4 | import { Toast } from '@/types'
5 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
6 |
7 | export const createMessageDefaultOnError = ({
8 | queryClient,
9 | addToast,
10 | threadContext,
11 | }: {
12 | queryClient: QueryClient
13 | addToast: (toast: Toast) => void
14 | threadContext: ReturnType
15 | }) => (error: any) => {
16 | if (error.name === 'AbortError') {
17 | queryClient.invalidateQueries({ queryKey: ['messages', threadContext.variables] })
18 | queryClient.invalidateQueries({ queryKey: ['runs', threadContext.variables] })
19 | return
20 | }
21 |
22 | addToast({ type: 'error', message: error.message })
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/lib/iconAvatars/iconAvatarComponents.ts:
--------------------------------------------------------------------------------
1 | import { IconAvatarName } from '@/lib/enums'
2 | import {
3 | BackpackIcon,
4 | RocketIcon,
5 | MagicWandIcon,
6 | CubeIcon,
7 | TargetIcon,
8 | DiscIcon,
9 | GlobeIcon,
10 | StarIcon,
11 | LightningBoltIcon,
12 | FaceIcon,
13 | PersonIcon,
14 | HeartIcon,
15 | } from '@radix-ui/react-icons'
16 |
17 | export const iconAvatarComponents = {
18 | [IconAvatarName.BACKPACK]: BackpackIcon,
19 | [IconAvatarName.ROCKET]: RocketIcon,
20 | [IconAvatarName.MAGIC_WAND]: MagicWandIcon,
21 | [IconAvatarName.CUBE]: CubeIcon,
22 | [IconAvatarName.TARGET]: TargetIcon,
23 | [IconAvatarName.DISC]: DiscIcon,
24 | [IconAvatarName.GLOBE]: GlobeIcon,
25 | [IconAvatarName.STAR]: StarIcon,
26 | [IconAvatarName.LIGHTNING_BOLT]: LightningBoltIcon,
27 | [IconAvatarName.FACE]: FaceIcon,
28 | [IconAvatarName.PERSON]: PersonIcon,
29 | [IconAvatarName.HEART]: HeartIcon,
30 | }
31 |
--------------------------------------------------------------------------------
/packages/react/src/lib/iframes/isIframe.ts:
--------------------------------------------------------------------------------
1 | export const isIframe = () => {
2 | if (typeof window === 'undefined') return false
3 |
4 | return window.self !== window.top
5 | }
6 |
--------------------------------------------------------------------------------
/packages/react/src/lib/markdown/escapeInvalidTagNames.ts:
--------------------------------------------------------------------------------
1 | const isValidTagName = (tagName: string): boolean => (
2 | /^[A-Za-z_][A-Za-z0-9_.:-]*$/.test(tagName)
3 | )
4 |
5 | export const escapeInvalidTagNames = (markdown: string): string => (
6 | markdown.replace(/<([^\s>/]+)([^>]*)>/g, (fullMatch, tagName) => (
7 | isValidTagName(tagName) ? fullMatch : fullMatch.replace(//g, '>')
8 | ))
9 | )
10 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/createMessageResponse/actionsStream.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import pMap from 'p-map'
3 |
4 | export const actionsStream = async ({
5 | client,
6 | run,
7 | handleToolCall,
8 | controller,
9 | }: {
10 | client: any
11 | run: OpenAI.Beta.Threads.Runs.Run
12 | handleToolCall: any
13 | controller: ReadableStreamDefaultController
14 | }) => {
15 | if (!run.required_action) {
16 | throw new Error('Run does not have a required action')
17 | }
18 |
19 | const toolCalls = run.required_action.submit_tool_outputs.tool_calls
20 |
21 | return client.beta.threads.runs.submitToolOutputsStream(
22 | run.thread_id,
23 | run.id,
24 | {
25 | tool_outputs: await pMap(toolCalls, (toolCall) => (
26 | handleToolCall({
27 | toolCall,
28 | run,
29 | controller,
30 | }))
31 | ),
32 | },
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/createMessageResponse/index.ts:
--------------------------------------------------------------------------------
1 | import { handleStream } from './handleStream'
2 |
3 | type CallbackArgs = {
4 | controller: ReadableStreamDefaultController
5 | }
6 |
7 | export const createMessageResponse = ({
8 | client,
9 | createRunStream,
10 | handleToolCall,
11 | onStart = () => {},
12 | onError = () => {},
13 | onClose = () => {},
14 | onEvent = () => {},
15 | }: {
16 | client: any
17 | createRunStream: any
18 | handleToolCall: any
19 | onStart?: (args: CallbackArgs) => void
20 | onError?: (args: CallbackArgs & { error: any }) => void
21 | onClose?: (args: CallbackArgs) => void
22 | onEvent?: (args: CallbackArgs & { event: string, data: any }) => void
23 | }) => (
24 | new ReadableStream({
25 | async start(controller) {
26 | onStart({ controller })
27 |
28 | try {
29 | await handleStream({
30 | client,
31 | stream: createRunStream,
32 | controller,
33 | handleToolCall,
34 | onEvent,
35 | })
36 | } catch (error) {
37 | onError({ error, controller })
38 | controller.error(error)
39 | }
40 |
41 | onClose({ controller })
42 | controller.close()
43 | },
44 | })
45 | )
46 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/extendMessage.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { getRunSteps } from '@/lib/runSteps/getRunSteps'
3 |
4 | type Args = {
5 | message: OpenAI.Beta.Threads.Messages.Message
6 | client: OpenAI
7 | }
8 |
9 | export const extendMessage = async ({
10 | message,
11 | client,
12 | }: Args) => {
13 | if (!message.run_id) {
14 | return {
15 | ...message,
16 | runSteps: [],
17 | }
18 | }
19 |
20 | return {
21 | ...message,
22 | runSteps: await getRunSteps({
23 | threadId: message.thread_id,
24 | runId: message.run_id,
25 | client,
26 | }),
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesQueryOptions.ts:
--------------------------------------------------------------------------------
1 | import { useQueryClient } from '@tanstack/react-query'
2 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
3 | import { useThreadContext } from '@/hooks/threads/useThreadContext'
4 | import { queryOptions } from '@/lib/threads/queryOptions'
5 |
6 | export const messagesQueryOptions = ({
7 | queryClient,
8 | threadContext,
9 | superinterfaceContext,
10 | }: {
11 | queryClient: ReturnType,
12 | threadContext: ReturnType,
13 | superinterfaceContext: ReturnType,
14 | }) => (
15 | queryOptions({
16 | queryKeyBase: ['messages'],
17 | path: '/messages',
18 | queryClient,
19 | threadContext,
20 | superinterfaceContext,
21 | })
22 | )
23 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesResponse/data/index.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { serializeMessage } from '@/lib/messages/serializeMessage'
3 | import { messages } from './messages'
4 |
5 | export const data = async ({
6 | messagesResponse,
7 | pageParam,
8 | threadId,
9 | client,
10 | }: {
11 | messagesResponse: OpenAI.CursorPage
12 | pageParam?: string
13 | threadId: string
14 | client: OpenAI
15 | }) => (
16 | (await messages({
17 | messagesResponse,
18 | pageParam,
19 | threadId,
20 | client,
21 | })).map((message) => (
22 | serializeMessage({ message })
23 | ))
24 | )
25 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesResponse/data/messages/index.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import pMap from 'p-map'
3 | import { extendMessage } from '@/lib/messages/extendMessage'
4 | import { runMessages } from './runMessages'
5 |
6 | export const messages = async ({
7 | messagesResponse,
8 | pageParam,
9 | threadId,
10 | client,
11 | }: {
12 | messagesResponse: OpenAI.CursorPage
13 | pageParam?: string
14 | threadId: string
15 | client: OpenAI
16 | }) => {
17 | const result = await pMap(messagesResponse.data, (message) => (
18 | extendMessage({
19 | client,
20 | message,
21 | })
22 | ))
23 |
24 | if (pageParam) {
25 | return result
26 | }
27 |
28 | return [
29 | ...await runMessages({
30 | result,
31 | threadId,
32 | client,
33 | }),
34 | ...result,
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesResponse/data/messages/runMessages/getLatestRun.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 |
3 | type Args = {
4 | threadId: string
5 | client: OpenAI
6 | }
7 |
8 | export const getLatestRun = async ({
9 | threadId,
10 | client,
11 | }: Args) => {
12 | const runsResponse = await client.beta.threads.runs.list(threadId, {
13 | limit: 1,
14 | })
15 |
16 | return runsResponse.data[0]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesResponse/hasNextPage.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { limit } from './limit'
3 |
4 | export const hasNextPage = ({
5 | messagesResponse,
6 | }: {
7 | messagesResponse: OpenAI.CursorPage
8 | }) => {
9 | if (messagesResponse.data.length < limit) return false
10 |
11 | return messagesResponse.hasNextPage()
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesResponse/index.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import OpenAI from 'openai'
3 | import { MessagesPage } from '@/types'
4 | import { data } from './data'
5 | import { limit } from './limit'
6 | import { hasNextPage } from './hasNextPage'
7 |
8 | type Args = {
9 | client: OpenAI
10 | threadId: string
11 | pageParam?: string
12 | }
13 |
14 | export const messagesResponse = async ({
15 | client,
16 | threadId,
17 | pageParam,
18 | }: Args): Promise => {
19 | const messagesResponse = await client.beta.threads.messages.list(threadId, {
20 | ...(pageParam ? { after: pageParam } : {}),
21 | limit,
22 | })
23 |
24 | return {
25 | data: await data({
26 | client,
27 | messagesResponse,
28 | pageParam,
29 | threadId,
30 | }),
31 | hasNextPage: hasNextPage({ messagesResponse }),
32 | // @ts-ignore-next-line
33 | lastId: messagesResponse.body.last_id,
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/messagesResponse/limit.ts:
--------------------------------------------------------------------------------
1 | export const limit = 10
2 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/order.ts:
--------------------------------------------------------------------------------
1 | import { sort } from 'radash'
2 | import { SerializedMessage } from '@/types'
3 |
4 | type Args = {
5 | messages: SerializedMessage[]
6 | }
7 |
8 | export const order = ({ messages }: Args) =>
9 | sort(messages, m => m.created_at, true)
10 |
--------------------------------------------------------------------------------
/packages/react/src/lib/messages/serializeMessage.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 | import { serializeRunStep } from '@/lib/runSteps/serializeRunStep'
3 |
4 | export const serializeMessage = ({
5 | message,
6 | }: {
7 | message: OpenAI.Beta.Threads.Messages.Message & {
8 | runSteps?: OpenAI.Beta.Threads.Runs.RunStep[]
9 | }
10 | }) => ({
11 | id: message.id,
12 | role: message.role,
13 | created_at: message.created_at,
14 | content: message.content,
15 | run_id: message.run_id,
16 | assistant_id: message.assistant_id,
17 | thread_id: message.thread_id,
18 | attachments: message.attachments,
19 | metadata: message.metadata,
20 | runSteps: (message.runSteps ?? []).map((runStep) => (
21 | serializeRunStep({ runStep })
22 | )),
23 | status: message.status ?? 'completed',
24 | })
25 |
--------------------------------------------------------------------------------
/packages/react/src/lib/misc/merge/customizer.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 |
3 | // @ts-ignore-next-line
4 | export const customizer = (objectValue, srcValue) => {
5 | if (!_.isArray(objectValue)) return
6 |
7 | return srcValue
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react/src/lib/misc/merge/index.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import { customizer } from './customizer'
3 |
4 | // @ts-ignore-next-line
5 | export const merge = (obj, ...sources) =>
6 | _.mergeWith(_.cloneDeep(obj), ...sources, customizer)
7 |
--------------------------------------------------------------------------------
/packages/react/src/lib/optimistic/isOptimistic.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 |
3 | type Args = {
4 | id: string
5 | }
6 |
7 | export const isOptimistic = ({
8 | id,
9 | }: Args) => (
10 | _.startsWith(id, '-')
11 | )
12 |
--------------------------------------------------------------------------------
/packages/react/src/lib/optimistic/optimisticId.ts:
--------------------------------------------------------------------------------
1 | import { uid } from 'radash'
2 |
3 | export const optimisticId = () => (
4 | `-${uid(24)}`
5 | )
6 |
--------------------------------------------------------------------------------
/packages/react/src/lib/runSteps/getRunSteps/index.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai'
2 |
3 | type Args = {
4 | threadId: string
5 | runId: string
6 | client: OpenAI
7 | }
8 |
9 | export const getRunSteps = async ({
10 | threadId,
11 | runId,
12 | client,
13 | }: Args) => {
14 | const runStepsResponse = await client.beta.threads.runs.steps.list(
15 | threadId,
16 | runId,
17 | )
18 |
19 | return runStepsResponse.data
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react/src/lib/runSteps/serializeRunStep.ts:
--------------------------------------------------------------------------------
1 | import { pick } from 'radash'
2 | import OpenAI from 'openai'
3 |
4 | export const serializeRunStep = ({
5 | runStep,
6 | }: {
7 | runStep: OpenAI.Beta.Threads.Runs.RunStep
8 | }) => (
9 | pick(runStep, [
10 | 'id',
11 | 'run_id',
12 | 'step_details',
13 | 'completed_at',
14 | 'cancelled_at',
15 | 'failed_at',
16 | 'status',
17 | ])
18 | )
19 |
--------------------------------------------------------------------------------
/packages/react/src/lib/runs/serializeRun.ts:
--------------------------------------------------------------------------------
1 | import { pick } from 'radash'
2 | import OpenAI from 'openai'
3 |
4 | export const serializeRun = ({
5 | run
6 | }: {
7 | run: OpenAI.Beta.Threads.Runs.Run
8 | }) => (
9 | pick(run, [
10 | 'id',
11 | 'thread_id',
12 | 'assistant_id',
13 | 'created_at',
14 | ])
15 | )
16 |
--------------------------------------------------------------------------------
/packages/react/src/lib/streams/enqueueJson.ts:
--------------------------------------------------------------------------------
1 | export const enqueueJson = ({
2 | controller,
3 | value,
4 | }: {
5 | controller: ReadableStreamDefaultController
6 | value: any
7 | }) => (
8 | controller.enqueue(new TextEncoder().encode(JSON.stringify(value)))
9 | )
10 |
--------------------------------------------------------------------------------
/packages/react/src/lib/superinterfaceCloud/baseUrl.ts:
--------------------------------------------------------------------------------
1 | export const baseUrl = 'https://superinterface.ai/api/cloud'
2 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/cookieOptions/get.ts:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 | import { key } from '@/lib/threadIdStorage/key'
3 |
4 | export const get = ({
5 | assistantId,
6 | }: {
7 | assistantId: string
8 | }) => (
9 | Cookies.get(key({ assistantId })) ?? null
10 | )
11 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/cookieOptions/index.ts:
--------------------------------------------------------------------------------
1 | import type { ThreadStorageOptions } from '@/types'
2 | import { get } from './get'
3 | import { set } from './set'
4 | import { remove } from './remove'
5 |
6 | export const cookieOptions: ThreadStorageOptions = {
7 | get,
8 | set,
9 | remove,
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/cookieOptions/remove.ts:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 | import { key } from '@/lib/threadIdStorage/key'
3 |
4 | export const remove = ({
5 | assistantId,
6 | }: {
7 | assistantId: string
8 | }) => (
9 | Cookies.remove(key({ assistantId }))
10 | )
11 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/cookieOptions/set.ts:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 | import { key } from '@/lib/threadIdStorage/key'
3 |
4 | export const set = ({
5 | assistantId,
6 | threadId,
7 | }: {
8 | assistantId: string
9 | threadId: string
10 | }) => (
11 | Cookies.set(key({ assistantId }), threadId)
12 | )
13 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/key.ts:
--------------------------------------------------------------------------------
1 | export const key = ({
2 | assistantId,
3 | }: {
4 | assistantId: string
5 | }) => (
6 | `superinterface-${assistantId}-threadId`
7 | )
8 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/localStorageOptions/get.ts:
--------------------------------------------------------------------------------
1 | import { key } from '@/lib/threadIdStorage/key'
2 |
3 | export const get = ({
4 | assistantId,
5 | }: {
6 | assistantId: string
7 | }) => (
8 | window.localStorage.getItem(key({ assistantId }))
9 | )
10 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/localStorageOptions/index.ts:
--------------------------------------------------------------------------------
1 | import type { ThreadStorageOptions } from '@/types'
2 | import { get } from './get'
3 | import { set } from './set'
4 | import { remove } from './remove'
5 |
6 | export const localStorageOptions: ThreadStorageOptions = {
7 | get,
8 | set,
9 | remove,
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/localStorageOptions/remove.ts:
--------------------------------------------------------------------------------
1 | import { key } from '@/lib/threadIdStorage/key'
2 |
3 | export const remove = ({
4 | assistantId,
5 | }: {
6 | assistantId: string
7 | }) => (
8 | window.localStorage.removeItem(key({ assistantId }))
9 | )
10 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threadIdStorage/localStorageOptions/set.ts:
--------------------------------------------------------------------------------
1 | import { key } from '@/lib/threadIdStorage/key'
2 |
3 | export const set = ({
4 | assistantId,
5 | threadId,
6 | }: {
7 | assistantId: string
8 | threadId: string
9 | }) => (
10 | window.localStorage.setItem(key({ assistantId }), threadId)
11 | )
12 |
--------------------------------------------------------------------------------
/packages/react/src/lib/threads/queryOptions/variableParams.ts:
--------------------------------------------------------------------------------
1 | import { useSuperinterfaceContext } from '@/hooks/core/useSuperinterfaceContext'
2 |
3 | export const variableParams = ({
4 | variables,
5 | superinterfaceContext,
6 | }: {
7 | variables: {
8 | [key: string]: any
9 | }
10 | superinterfaceContext: ReturnType
11 | }) => {
12 | if (variables.threadId) return variables
13 | if (!variables.assistantId) return variables
14 | if (!superinterfaceContext.threadIdStorageOptions?.get) return variables
15 |
16 | const threadId = superinterfaceContext.threadIdStorageOptions.get({ assistantId: variables.assistantId })
17 | if (!threadId) return variables
18 |
19 | return {
20 | ...variables,
21 | threadId,
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react/src/server.ts:
--------------------------------------------------------------------------------
1 | export { messagesResponse } from '@/lib/messages/messagesResponse'
2 | export { createMessageResponse } from '@/lib/messages/createMessageResponse'
3 | export { extendMessage } from '@/lib/messages/extendMessage'
4 |
--------------------------------------------------------------------------------
/packages/react/src/utils.ts:
--------------------------------------------------------------------------------
1 | export { optimisticId } from '@/lib/optimistic/optimisticId'
2 | export { isOptimistic } from '@/lib/optimistic/isOptimistic'
3 | export { serializeMessage } from '@/lib/messages/serializeMessage'
4 | export { key as threadIdStorageKey } from '@/lib/threadIdStorage/key'
5 | export { enqueueJson } from '@/lib/streams/enqueueJson'
6 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "declaration": true,
6 | "noEmit": false,
7 | "paths": {
8 | "@/*": ["./src/*"]
9 | }
10 | },
11 | "include": ["src"],
12 | "exclude": ["node_modules", "dist"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/react/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 | import babel from 'esbuild-plugin-babel'
3 |
4 | const ReactCompilerConfig = {
5 | target: '18'
6 | }
7 |
8 | export default defineConfig({
9 | entry: [
10 | 'src/*.ts',
11 | 'src/types/*.ts',
12 | ],
13 | esbuildPlugins: [
14 | babel({
15 | config: {
16 | presets: [
17 | [
18 | '@babel/preset-env',
19 | {
20 | targets: {
21 | esmodules: true,
22 | },
23 | },
24 | ],
25 | '@babel/preset-typescript',
26 | [
27 | '@babel/preset-react',
28 | {
29 | runtime: 'automatic',
30 | },
31 | ],
32 | ],
33 | plugins: [
34 | ['babel-plugin-react-compiler', ReactCompilerConfig],
35 | ],
36 | },
37 | }),
38 | ],
39 | splitting: false,
40 | sourcemap: true,
41 | clean: true,
42 | format: [
43 | 'esm',
44 | 'cjs',
45 | ],
46 | dts: true,
47 | external: [
48 | 'react',
49 | 'react-dom',
50 | '@tanstack/react-query',
51 | ],
52 | })
53 |
--------------------------------------------------------------------------------
/packages/root-element/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@superinterface/root-element",
3 | "version": "1.0.2",
4 | "main": "./dist/index.js",
5 | "module": "./dist/index.mjs",
6 | "types": "./dist/index.d.ts",
7 | "exports": {
8 | ".": "./dist/index.js",
9 | "./*": "./dist/*.js"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "keywords": [],
15 | "license": "ISC",
16 | "scripts": {
17 | "build": "tsup",
18 | "metafile": "tsup --metafile"
19 | },
20 | "devDependencies": {
21 | "tsup": "^8.4.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/root-element/src/index.ts:
--------------------------------------------------------------------------------
1 | export { rootElement } from '@/lib/rootElement'
2 |
--------------------------------------------------------------------------------
/packages/root-element/src/lib/rootElement/manualElement.ts:
--------------------------------------------------------------------------------
1 | import { scriptTagId as getScriptTagId } from './scriptTagId'
2 |
3 | export const manualElement = ({
4 | currentScript,
5 | }: {
6 | currentScript: HTMLScriptElement
7 | }) => {
8 | const scriptTagId = getScriptTagId({ currentScript })
9 |
10 | if (!scriptTagId) {
11 | return null
12 | }
13 |
14 | return document.querySelector(`.superinterface-root[data-script-tag-id="${scriptTagId}"]`)
15 | }
16 |
--------------------------------------------------------------------------------
/packages/root-element/src/lib/rootElement/scriptTagId.ts:
--------------------------------------------------------------------------------
1 | export const scriptTagId = ({
2 | currentScript,
3 | }: {
4 | currentScript: HTMLScriptElement
5 | }) => {
6 | const url = new URL(currentScript.src)
7 | const scriptTagIdMatch = url.pathname.match(/^\/script-tags\/(.+)$/)
8 |
9 | if (!scriptTagIdMatch) {
10 | return null
11 | }
12 |
13 | return scriptTagIdMatch[1] ?? null
14 | }
15 |
--------------------------------------------------------------------------------
/packages/root-element/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "declaration": false,
6 | "noEmit": false,
7 | "paths": {
8 | "@/*": ["./src/*"]
9 | }
10 | },
11 | "include": ["src"],
12 | "exclude": ["node_modules", "dist"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/root-element/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | entry: [
5 | 'src/index.ts',
6 | ],
7 | splitting: false,
8 | sourcemap: true,
9 | clean: true,
10 | format: [
11 | 'esm',
12 | 'cjs',
13 | ],
14 | dts: true,
15 | })
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "ESNext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "react-jsx"
17 | },
18 | "include": ["packages/*/src"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "build": {
5 | "dependsOn": [
6 | "^build"
7 | ],
8 | "outputs": [
9 | "dist/**"
10 | ],
11 | "inputs": [
12 | "src/**/*.tsx",
13 | "src/**/*.ts"
14 | ]
15 | },
16 | "deploy": {
17 | "dependsOn": [
18 | "build",
19 | "test",
20 | "lint"
21 | ]
22 | },
23 | "test": {
24 | "dependsOn": [
25 | "build"
26 | ],
27 | "inputs": [
28 | "src/**/*.tsx",
29 | "src/**/*.ts",
30 | "test/**/*.ts",
31 | "test/**/*.tsx"
32 | ]
33 | },
34 | "lint": {},
35 | "dev": {
36 | "cache": false,
37 | "persistent": true
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------