+
{filteredThreads.map((thread: Thread, key) => (
-
+
))}
)
}
-type BrowseListProps = {
+type ThreadListProps = {
initialThreads: Thread[]
}
-
-const excerpt =
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed id mauris rhoncus, imperdiet dui a, viverra eros. Nullam eget metus ante. Etiam maximus erat ut libero rutrum, non cursus sapien condimentum. Quisque ultricies suscipit augue eu aliquam. Donec fringilla tristique vestibulum.'
-const question = 'why is the sky blue?'
diff --git a/apps/masterbots.ai/components/browse/browse-chat-message.tsx b/apps/masterbots.ai/components/shared/thread-message.tsx
similarity index 82%
rename from apps/masterbots.ai/components/browse/browse-chat-message.tsx
rename to apps/masterbots.ai/components/shared/thread-message.tsx
index ce6f45e0..722b7e3b 100644
--- a/apps/masterbots.ai/components/browse/browse-chat-message.tsx
+++ b/apps/masterbots.ai/components/shared/thread-message.tsx
@@ -1,16 +1,11 @@
-// Inspired by Chatbot-UI and modified to fit the needs of this project
-// @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Chat/ChatcleanMessage.tsx
-
import type { Message } from 'ai'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
-import Image from 'next/image'
import type { Chatbot } from '@repo/mb-genql'
-import { cleanPrompt, cn } from '@/lib/utils'
+import { cn } from '@/lib/utils'
import { CodeBlock } from '@/components/ui/codeblock'
-import { MemoizedReactMarkdown } from '@/components/markdown'
-import { IconOpenAI, IconUser } from '@/components/ui/icons'
-import { ChatMessageActions } from '../c/chat-message-actions'
+import { MemoizedReactMarkdown } from '@/components/shared/markdown'
+import { cleanPrompt } from '@/lib/threads'
export interface ChatMessageProps {
message: Message
diff --git a/apps/masterbots.ai/components/shared/thread-dialog/thread-excerpt.tsx b/apps/masterbots.ai/components/shared/thread-short-message.tsx
similarity index 93%
rename from apps/masterbots.ai/components/shared/thread-dialog/thread-excerpt.tsx
rename to apps/masterbots.ai/components/shared/thread-short-message.tsx
index 547a4c66..6ff0a501 100644
--- a/apps/masterbots.ai/components/shared/thread-dialog/thread-excerpt.tsx
+++ b/apps/masterbots.ai/components/shared/thread-short-message.tsx
@@ -1,7 +1,7 @@
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
-import { MemoizedReactMarkdown } from '../../markdown'
-import { CodeBlock } from '../../ui/codeblock'
+import { MemoizedReactMarkdown } from './markdown'
+import { CodeBlock } from '../ui/codeblock'
export function ShortMessage({ content }: { content: string }) {
return (
diff --git a/apps/masterbots.ai/hooks/use-thread.tsx b/apps/masterbots.ai/hooks/use-thread.tsx
index cc8bb50a..5befd2d4 100644
--- a/apps/masterbots.ai/hooks/use-thread.tsx
+++ b/apps/masterbots.ai/hooks/use-thread.tsx
@@ -12,7 +12,7 @@ import { Message as AIMessage } from 'ai'
import { uniqBy } from 'lodash'
import toast from 'react-hot-toast'
import { Chatbot, Message, Thread } from '@repo/mb-genql'
-import { getAllUserMessagesAsStringArray } from '@/components/c/chat'
+import { getAllUserMessagesAsStringArray } from '@/components/routes/c/chat'
import { useSidebar } from './use-sidebar'
import { useScroll } from 'framer-motion'
import { useAtBottom } from './use-at-bottom'
diff --git a/apps/masterbots.ai/lib/animation.ts b/apps/masterbots.ai/lib/animation.ts
new file mode 100644
index 00000000..3cfcd9df
--- /dev/null
+++ b/apps/masterbots.ai/lib/animation.ts
@@ -0,0 +1,34 @@
+// Easing function for smooth animation
+export const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
+ t /= d / 2
+ if (t < 1) return (c / 2) * t * t + b
+ t--
+ return (-c / 2) * (t * (t - 2) - 1) + b
+}
+
+let animationFrameId: number
+export const scrollToBottomOfElement = (element?: HTMLElement) => {
+ if (!element) return
+ const targetScroll = element.scrollHeight - element.clientHeight
+ const duration = 500
+ const startTime = performance.now()
+
+ const animateScroll = (currentTime: number) => {
+ const elapsed = currentTime - startTime
+ const position = easeInOutQuad(
+ elapsed,
+ element.scrollTop,
+ targetScroll - element.scrollTop,
+ duration
+ )
+ element.scrollTop = position
+
+ if (elapsed < duration) {
+ animationFrameId = requestAnimationFrame(animateScroll)
+ } else {
+ cancelAnimationFrame(animationFrameId)
+ }
+ }
+
+ animationFrameId = requestAnimationFrame(animateScroll)
+}
diff --git a/apps/masterbots.ai/lib/threads.ts b/apps/masterbots.ai/lib/threads.ts
new file mode 100644
index 00000000..fab3891e
--- /dev/null
+++ b/apps/masterbots.ai/lib/threads.ts
@@ -0,0 +1,71 @@
+import type * as AI from 'ai'
+import { Message } from '@repo/mb-genql'
+import { type Message as AIMessage } from 'ai/react'
+
+export function createMessagePairs(messages: Message[] | AIMessage[]) {
+ const messagePairs: MessagePair[] = []
+
+ for (let i = 0; i < messages.length; i++) {
+ const message = messages[i]
+
+ if (message.role === 'user') {
+ const userMessage = message
+ const chatGptMessages = []
+ for (let j = i + 1; j < messages.length; j++) {
+ const chatGptMessage = findNextAssistantMessage(messages, j)
+ if (!chatGptMessage) {
+ break
+ } else {
+ chatGptMessages.push(chatGptMessage)
+ continue
+ }
+ }
+ messagePairs.push({
+ userMessage,
+ chatGptMessage: chatGptMessages
+ })
+ }
+ }
+
+ return messagePairs
+}
+
+const findNextAssistantMessage = (
+ messages: Message[] | AIMessage[],
+ startIndex: number
+) => {
+ if (messages[startIndex].role === 'assistant') {
+ return {
+ ...messages[startIndex],
+ content: cleanPrompt(messages[startIndex].content)
+ }
+ }
+ return null
+}
+
+// From chat-message.tsx
+export function cleanPrompt(str: string) {
+ const marker = ']. Then answer this question:'
+ const index = str.indexOf(marker)
+ let extracted = ''
+
+ if (index !== -1) {
+ extracted = str.substring(index + marker.length)
+ }
+ // console.log('cleanPrompt', str, extracted, index)
+ return extracted || str
+}
+
+export interface MessagePair {
+ userMessage: Message | AI.Message
+ chatGptMessage: Message[]
+}
+
+export function convertMessage(message: Message) {
+ return {
+ id: message.messageId,
+ content: message.content,
+ createAt: message.createdAt,
+ role: message.role
+ } as AI.Message
+}
diff --git a/apps/masterbots.ai/lib/utils.ts b/apps/masterbots.ai/lib/utils.ts
index 0783e965..ccacf839 100644
--- a/apps/masterbots.ai/lib/utils.ts
+++ b/apps/masterbots.ai/lib/utils.ts
@@ -1,5 +1,3 @@
-import { type Message as AIMessage } from 'ai/react'
-import type { Message } from '@repo/mb-genql'
import { clsx, ClassValue } from 'clsx'
import { customAlphabet } from 'nanoid'
import { twMerge } from 'tailwind-merge'
@@ -70,61 +68,6 @@ export function extractBetweenMarkers(
return str.substring(startIndex, endIndex).trim()
}
-// From browse-list.tsx
-export function createMessagePairs(messages: Message[] | AIMessage[]) {
- const messagePairs = []
-
- for (let i = 0; i < messages.length; i++) {
- const message = messages[i]
-
- if (message.role === 'user') {
- const userMessage = message
- const chatGptMessages = []
- for (let j = i + 1; j < messages.length; j++) {
- const chatGptMessage = findNextAssistantMessage(messages, j)
- if (!chatGptMessage) {
- break
- } else {
- chatGptMessages.push(chatGptMessage)
- continue
- }
- }
- messagePairs.push({
- userMessage,
- chatGptMessage: chatGptMessages
- })
- }
- }
-
- return messagePairs
-}
-
-const findNextAssistantMessage = (
- messages: Message[] | AIMessage[],
- startIndex: number
-) => {
- if (messages[startIndex].role === 'assistant') {
- return {
- ...messages[startIndex],
- content: cleanPrompt(messages[startIndex].content)
- }
- }
- return null
-}
-
-// From chat-message.tsx
-export function cleanPrompt(str: string) {
- const marker = ']. Then answer this question:'
- const index = str.indexOf(marker)
- let extracted = ''
-
- if (index !== -1) {
- extracted = str.substring(index + marker.length)
- }
- // console.log('cleanPrompt', str, extracted, index)
- return extracted || str
-}
-
export const readingTime = (messages: { content: string }[]) => {
let contentGroup: any = []
@@ -141,41 +84,6 @@ export const readingTime = (messages: { content: string }[]) => {
return time
}
-// Easing function for smooth animation
-export const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
- t /= d / 2
- if (t < 1) return (c / 2) * t * t + b
- t--
- return (-c / 2) * (t * (t - 2) - 1) + b
-}
-
-let animationFrameId: number
-export const scrollToBottomOfElement = (element?: HTMLElement) => {
- if (!element) return
- const targetScroll = element.scrollHeight - element.clientHeight
- const duration = 500
- const startTime = performance.now()
-
- const animateScroll = (currentTime: number) => {
- const elapsed = currentTime - startTime
- const position = easeInOutQuad(
- elapsed,
- element.scrollTop,
- targetScroll - element.scrollTop,
- duration
- )
- element.scrollTop = position
-
- if (elapsed < duration) {
- animationFrameId = requestAnimationFrame(animateScroll)
- } else {
- cancelAnimationFrame(animationFrameId)
- }
- }
-
- animationFrameId = requestAnimationFrame(animateScroll)
-}
-
export async function sleep(time: number) {
return new Promise(resolve => setTimeout(resolve, time))
}
diff --git a/apps/masterbots.ai/package.json b/apps/masterbots.ai/package.json
index 2b68566e..ffd1e0f9 100644
--- a/apps/masterbots.ai/package.json
+++ b/apps/masterbots.ai/package.json
@@ -36,6 +36,7 @@
"@repo/mb-lib": "workspace:*",
"@repo/mb-types": "workspace:*",
"@supabase/ssr": "^0.1.0",
+ "@tanstack/react-query": "^5.29.0",
"@vercel/analytics": "^1.1.1",
"@vercel/og": "^0.5.20",
"ai": "^2.2.25",
diff --git a/apps/masterbots.ai/services/hasura/hasura.service.ts b/apps/masterbots.ai/services/hasura/hasura.service.ts
index e871f0a2..35d85245 100644
--- a/apps/masterbots.ai/services/hasura/hasura.service.ts
+++ b/apps/masterbots.ai/services/hasura/hasura.service.ts
@@ -20,6 +20,7 @@ import {
SaveNewMessageParams,
UpsertUserParams
} from './hasura.service.type'
+import { createMessagePairs } from '@/lib/threads'
function getHasuraClient({ jwt, adminSecret }: GetHasuraClientParams) {
return createMbClient({
@@ -541,3 +542,8 @@ export async function getUser({
})
return user[0]
}
+
+export async function getMessagePairs(threadId) {
+ const messages = await getMessages({ threadId })
+ return createMessagePairs(messages)
+}
diff --git a/bun.lockb b/bun.lockb
index 1fa6a41f..21507a16 100755
Binary files a/bun.lockb and b/bun.lockb differ