)
}
diff --git a/apps/masterbots.ai/app/(browse)/page.tsx b/apps/masterbots.ai/app/(browse)/page.tsx
index 719ed596..9c39ccbf 100644
--- a/apps/masterbots.ai/app/(browse)/page.tsx
+++ b/apps/masterbots.ai/app/(browse)/page.tsx
@@ -1,21 +1,18 @@
-import BrowseList from '@/components/browse/browse-list'
-import { BrowseCategoryTabs } from '@/components/browse/browse-category-tabs'
-import { BrowseSearchInput } from '@/components/browse/browse-search-input'
+import ThreadList from '@/components/shared/thread-list'
+import { CategoryTabs } from '@/components/shared/category-tabs/category-tabs'
+import { BrowseInput } from '@/components/shared/browse-input'
import { getBrowseThreads, getCategories } from '@/services/hasura'
-export const revalidate = 3600 // revalidate the data at most every hour
-
export default async function BrowsePage() {
const categories = await getCategories()
const threads = await getBrowseThreads({
limit: 50
})
-
return (
-
-
-
-
+
+
+
+
)
}
diff --git a/apps/masterbots.ai/app/b/[id]/page.tsx b/apps/masterbots.ai/app/b/[id]/page.tsx
index f0f8b3e4..e9fc5ccd 100644
--- a/apps/masterbots.ai/app/b/[id]/page.tsx
+++ b/apps/masterbots.ai/app/b/[id]/page.tsx
@@ -1,15 +1,16 @@
-import { getChatbot, getBrowseThreads } from '@/services/hasura'
+import { getChatbot, getBrowseThreads, getCategories } from '@/services/hasura'
import { botNames } from '@/lib/bots-names'
-import BrowseChatbotDetails from '@/components/browse/browse-chatbot-details'
-import BrowseSpecificThreadList from '@/components/browse/browse-specific-thread-list'
-
-const PAGE_SIZE = 50
+import ThreadList from '@/components/shared/thread-list'
+import AccountDetails from '@/components/shared/account-details'
+import { CategoryTabs } from '@/components/shared/category-tabs/category-tabs'
+import { BrowseInput } from '@/components/shared/browse-input'
export default async function BotThreadsPage({
params
}: {
params: { id: string }
}) {
+ const categories = await getCategories()
let chatbot, threads
chatbot = await getChatbot({
@@ -18,24 +19,27 @@ export default async function BotThreadsPage({
threads: true
})
if (!chatbot) throw new Error(`Chatbot ${botNames.get(params.id)} not found`)
-
- // session will always be defined
+ const chatbotName = botNames.get(params.id)
threads = await getBrowseThreads({
- chatbotName: botNames.get(params.id),
- limit: PAGE_SIZE
+ chatbotName,
+ limit: 50
})
return (
-
- {chatbot ?
: ''}
-
+
+
+
+
+
+
)
}
diff --git a/apps/masterbots.ai/app/c/[chatbot]/[threadId]/page.tsx b/apps/masterbots.ai/app/c/[chatbot]/[threadId]/page.tsx
index 3403606f..c7d48640 100644
--- a/apps/masterbots.ai/app/c/[chatbot]/[threadId]/page.tsx
+++ b/apps/masterbots.ai/app/c/[chatbot]/[threadId]/page.tsx
@@ -2,7 +2,7 @@ import { redirect } from 'next/navigation'
import type { Message } from 'ai/react'
import { isTokenExpired } from '@repo/mb-lib'
import { cookies } from 'next/headers'
-import { Chat } from '@/components/c/chat'
+import { Chat } from '@/components/routes/c/chat'
import { getThread } from '@/services/hasura'
import { getUserProfile } from '@/services/supabase'
diff --git a/apps/masterbots.ai/app/c/[chatbot]/page.tsx b/apps/masterbots.ai/app/c/[chatbot]/page.tsx
index 970aad53..0141b9e1 100644
--- a/apps/masterbots.ai/app/c/[chatbot]/page.tsx
+++ b/apps/masterbots.ai/app/c/[chatbot]/page.tsx
@@ -3,8 +3,8 @@ import { isTokenExpired } from '@repo/mb-lib'
import { nanoid } from 'nanoid'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
-import { ChatChatbot } from '@/components/c/chat-chatbot'
-import ThreadPanel from '@/components/c/thread-panel'
+import { ChatChatbot } from '@/components/routes/c/chat-chatbot'
+import ThreadPanel from '@/components/routes/c/thread-panel'
import { botNames } from '@/lib/bots-names'
import { getChatbot, getThreads } from '@/services/hasura'
import { getUserProfile } from '@/services/supabase'
diff --git a/apps/masterbots.ai/app/c/layout.tsx b/apps/masterbots.ai/app/c/layout.tsx
index 86ab7fd8..54821f7b 100644
--- a/apps/masterbots.ai/app/c/layout.tsx
+++ b/apps/masterbots.ai/app/c/layout.tsx
@@ -1,5 +1,5 @@
-import { ChatLayoutSection } from '@/components/c/chat-layout-section'
-import { ResponsiveSidebar } from '@/components/c/sidebar/sidebar-responsive'
+import { ChatLayoutSection } from '@/components/routes/c/chat-layout-section'
+import { ResponsiveSidebar } from '@/components/routes/c/sidebar/sidebar-responsive'
import FooterCT from '@/components/layout/footer-ct'
interface ChatLayoutProps {
diff --git a/apps/masterbots.ai/app/c/page.tsx b/apps/masterbots.ai/app/c/page.tsx
index 1979a52f..d0917350 100644
--- a/apps/masterbots.ai/app/c/page.tsx
+++ b/apps/masterbots.ai/app/c/page.tsx
@@ -1,8 +1,8 @@
import { isTokenExpired } from '@repo/mb-lib'
import { redirect } from 'next/navigation'
import { cookies } from 'next/headers'
-import ChatThreadListPanel from '@/components/c/chat-thread-list-panel'
-import ThreadPanel from '@/components/c/thread-panel'
+import ChatThreadListPanel from '@/components/routes/c/chat-thread-list-panel'
+import ThreadPanel from '@/components/routes/c/thread-panel'
import { getThreads } from '@/services/hasura'
import { getUserProfile } from '@/services/supabase'
diff --git a/apps/masterbots.ai/app/globals.css b/apps/masterbots.ai/app/globals.css
index fe0cea82..741d1bc0 100644
--- a/apps/masterbots.ai/app/globals.css
+++ b/apps/masterbots.ai/app/globals.css
@@ -100,32 +100,21 @@
}
.scrollbar {
- overflow: auto;
+ overflow: auto;
}
.scrollbar::-webkit-scrollbar {
- width: 4px;
- height: 4px;
+ width: 1px;
+ height: 1px;
}
.scrollbar::-webkit-scrollbar-track,
.scrollbar::-webkit-scrollbar-corner {
- background: var(--scrollbar-track) !important;
+ background: var(--scrollbar-track);
}
.scrollbar::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
border-radius: 2px;
}
-/* .scrollbar::-webkit-scrollbar-thumb:hover {
- background: var(--scrollbar-thumb-hover);
-} */
-
-@media screen and (min-width: 1024px) {
- .scrollbar::-webkit-scrollbar {
- width: 8px;
- height: 8px;
- }
-}
-
.scrollbar.small-thumb::-webkit-scrollbar-thumb {
border-left: 300px solid #f9f9fa;
@@ -151,3 +140,7 @@
overflow: visible;
text-overflow: clip;
}
+
+.hide-buttons > button {
+ display: none;
+}
diff --git a/apps/masterbots.ai/app/layout.tsx b/apps/masterbots.ai/app/layout.tsx
index e3441711..6040683b 100644
--- a/apps/masterbots.ai/app/layout.tsx
+++ b/apps/masterbots.ai/app/layout.tsx
@@ -9,9 +9,18 @@ import { Providers } from '@/components/layout/providers'
import { cn } from '@/lib/utils'
import { GlobalStoreProvider } from '@/hooks/use-global-store'
-export default async function RootLayout({ children }: RootLayoutProps) {
+async function getCookieData(): Promise<{ hasuraJwt; userProfile }> {
const hasuraJwt = cookies().get('hasuraJwt')?.value || ''
const userProfile = cookies().get('userProfile')?.value || null
+ return new Promise(resolve =>
+ setTimeout(() => {
+ resolve({ hasuraJwt, userProfile })
+ }, 1000)
+ )
+}
+
+export default async function RootLayout({ children }: RootLayoutProps) {
+ const { hasuraJwt, userProfile } = await getCookieData()
return (
diff --git a/apps/masterbots.ai/app/p/page.tsx b/apps/masterbots.ai/app/p/page.tsx
index d2325d98..2086576f 100644
--- a/apps/masterbots.ai/app/p/page.tsx
+++ b/apps/masterbots.ai/app/p/page.tsx
@@ -1,5 +1,5 @@
import { Suspense } from 'react'
-import { WorkEarlyAccessForm } from '@/components/p/early-access-from'
+import { WorkEarlyAccessForm } from '@/components/routes/p/early-access-from'
export default function WorkPage() {
return (
diff --git a/apps/masterbots.ai/app/u/[slug]/page.tsx b/apps/masterbots.ai/app/u/[slug]/page.tsx
index 729d66ea..b68cbd06 100644
--- a/apps/masterbots.ai/app/u/[slug]/page.tsx
+++ b/apps/masterbots.ai/app/u/[slug]/page.tsx
@@ -1,31 +1,39 @@
-import { getBrowseThreads, getUserInfoFromBrowse } from '@/services/hasura'
-import BrowseUserDetails from '@/components/browse/browse-user-details'
-import BrowseSpecificThreadList from '@/components/browse/browse-specific-thread-list'
-
-const PAGE_SIZE = 50
+import {
+ getBrowseThreads,
+ getCategories,
+ getUserInfoFromBrowse
+} from '@/services/hasura'
+import ThreadList from '@/components/shared/thread-list'
+import AccountDetails from '@/components/shared/account-details'
+import { CategoryTabs } from '@/components/shared/category-tabs/category-tabs'
+import { BrowseInput } from '@/components/shared/browse-input'
export default async function BotThreadsPage({
params
}: {
params: { slug: string }
}) {
+ const categories = await getCategories()
const user = await getUserInfoFromBrowse(params.slug)
if (!user) return
No user found.
const threads = await getBrowseThreads({
slug: params.slug,
- limit: PAGE_SIZE
+ limit: 50
})
return (
-
)
}
diff --git a/apps/masterbots.ai/components/browse/browse-chat-message-list.tsx b/apps/masterbots.ai/components/browse/browse-chat-message-list.tsx
deleted file mode 100644
index efa2afbf..00000000
--- a/apps/masterbots.ai/components/browse/browse-chat-message-list.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 { Chatbot, Message, User } from '@repo/mb-genql'
-import Image from 'next/image'
-import Link from 'next/link'
-import React from 'react'
-import { cn, createMessagePairs } from '@/lib/utils'
-import { IconUser } from '@/components/ui/icons'
-import { ChatAccordion } from '../shared/chat-accordion'
-import type { MessagePair } from './browse-chat-messages'
-import { convertMessage } from './browse-chat-messages'
-import { BrowseChatMessage } from './browse-chat-message'
-
-export function BrowseChatMessageList({
- messages,
- user,
- chatbot,
- isThread = false
-}: {
- messages: Message[]
- user?: User
- chatbot?: Chatbot
- isThread?: boolean
-}) {
- const [pairs, setPairs] = React.useState
([])
-
- React.useEffect(() => {
- if (messages.length) {
- const prePairs: MessagePair[] = createMessagePairs(
- messages
- ) as MessagePair[]
- setPairs(prePairs)
- } else setPairs([])
- }, [messages])
-
- return (
-
- {pairs.map((pair: MessagePair, key: number) => (
-
- {/* Thread Title */}
- {key !== 0 || isThread ? (
-
-
- {pair.userMessage.content}
-
-
- ) : null}
-
- {/* Thread Description */}
- <>>
-
- {/* Thread Content */}
-
- {pair.chatGptMessage.length > 0
- ? pair.chatGptMessage.map((message, index) => (
-
- ))
- : ''}
-
-
- ))}
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/browse-chat-messages.tsx b/apps/masterbots.ai/components/browse/browse-chat-messages.tsx
deleted file mode 100644
index c219f4c5..00000000
--- a/apps/masterbots.ai/components/browse/browse-chat-messages.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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 * as AI from 'ai'
-import type { Chatbot, Message, User } from '@repo/mb-genql'
-import React from 'react'
-import { getMessages } from '@/services/hasura'
-import BrowseChatbotDetails from './browse-chatbot-details'
-import { BrowseChatMessageList } from './browse-chat-message-list'
-
-export interface MessagePair {
- userMessage: Message
- chatGptMessage: Message[]
-}
-
-export function convertMessage(message: Message) {
- return {
- id: message.messageId,
- content: message.content,
- createAt: message.createdAt,
- role: message.role
- } as AI.Message
-}
-
-export function BrowseChatMessages({
- threadId,
- user,
- chatbot
-}: {
- threadId: string
- user?: User
- chatbot?: Chatbot
-}) {
- const [messages, setMessages] = React.useState([])
- const fetchMessages = async () => {
- if (threadId && !messages.length) {
- const messages = await getMessages({ threadId })
- setMessages(messages)
- }
- }
- React.useEffect(() => {
- fetchMessages()
- }, [threadId])
-
- return (
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/browse-chatbot-details.tsx b/apps/masterbots.ai/components/browse/browse-chatbot-details.tsx
deleted file mode 100644
index 1145d029..00000000
--- a/apps/masterbots.ai/components/browse/browse-chatbot-details.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import type { Chatbot } from '@repo/mb-genql'
-import Image from 'next/image'
-import Link from 'next/link'
-import { Separator } from '../ui/separator'
-
-export default function BrowseChatbotDetails({
- chatbot
-}: {
- chatbot?: Chatbot
-}) {
- return (
-
-
-
-
{chatbot.name}
-
-
- {chatbot.categories[0].category.name}.
-
-
-
- {chatbot.description ?
{chatbot.description}
: ''}
-
-
- Threads:{' '}
-
- {chatbot.threads.length ?? 1}
-
- {/*
- Views: 0
-
*/}
- {/*
- Read time:{' '}
-
- {readingTime(messages)} min
-
-
*/}
-
-
-
-
-
-
- Chat with {chatbot.name} >
-
- {/*
-
- 0
-
- 0
- {
- console.log('Share action required')
- }}
- variant="ghost"
- >
-
-
-
-
*/}
-
-
-
-
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/browse-list-item.tsx b/apps/masterbots.ai/components/browse/browse-list-item.tsx
deleted file mode 100644
index 1503120e..00000000
--- a/apps/masterbots.ai/components/browse/browse-list-item.tsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import Image from 'next/image'
-import { Message, Thread } from '@repo/mb-genql'
-import Link from 'next/link'
-import { useRouter } from 'next/navigation'
-import React from 'react'
-import { cn, sleep } from '@/lib/utils'
-import { getMessages } from '@/services/hasura'
-import { ChatAccordion } from '../shared/chat-accordion'
-import { ShortMessage } from '../short-message'
-import { IconOpenAI, IconUser } from '../ui/icons'
-import { BrowseChatMessageList } from './browse-chat-message-list'
-
-export default function BrowseListItem({
- thread,
- loadMore,
- loading,
- isLast,
- hasMore,
- pageType = ''
-}: {
- thread: Thread
- loadMore: () => void
- loading: boolean
- isLast: boolean
- hasMore: boolean
- pageType?: string
-}) {
- const threadRef = React.useRef(null)
- const router = useRouter()
- const [messages, setMessages] = React.useState([])
- // ! Move to custom hook and add it to the context useThread + useProvider @bran18
- const [isAccordionOpen, setIsAccordionOpen] = React.useState(false)
-
- React.useEffect(() => {
- if (!threadRef.current) return
- const observer = new IntersectionObserver(([entry]) => {
- if (hasMore && isLast && entry.isIntersecting && !loading) {
- const timeout = setTimeout(() => {
- loadMore()
- clearTimeout(timeout)
- }, 150)
-
- observer.unobserve(entry.target)
- }
- })
-
- observer.observe(threadRef.current)
-
- return () => {
- observer.disconnect()
- }
- }, [isLast, hasMore, loading, loadMore])
-
- const fetchMessages = async () => {
- const messages = await getMessages({ threadId: thread.threadId })
- setMessages(_prev => messages)
- }
-
- const handleAccordionToggle = async (isOpen: boolean) => {
- if (isOpen) {
- setMessages(_prev => [])
- await fetchMessages()
- }
- // When toggling accordion, it should scroll
- // Use optional chaining to ensure scrollIntoView is called only if current is not null
- await sleep(300) // animation time
- threadRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' })
- setIsAccordionOpen(isOpen)
- // Should fetch messages only when opening thread.
- }
-
- const goToThread = () => {
- router.push(
- `/b/${thread.chatbot.name.trim().toLowerCase()}/${thread.threadId}`
- )
- router.refresh()
- }
-
- return (
-
-
- {/* Thread Title */}
-
- {pageType !== 'bot' && thread.chatbot.avatar ? (
-
-
-
- ) : (
- pageType !== 'bot' && (
-
-
-
- )
- )}
-
-
- {thread.messages[0]?.content}
-
- {pageType !== 'user' && (
-
by
- )}
- {pageType !== 'user' && thread.user.profilePicture ? (
-
-
-
- ) : (
- pageType !== 'user' && (
-
-
-
- )
- )}
-
-
-
- {/* Thread Description */}
-
-
- {thread.messages[1]?.content &&
- thread.messages[1]?.role !== 'user' ? (
-
-
-
- ) : (
- ''
- )}
-
-
- {/* Thread Content */}
-
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/browse-list.tsx b/apps/masterbots.ai/components/browse/browse-list.tsx
deleted file mode 100644
index 7d0f9559..00000000
--- a/apps/masterbots.ai/components/browse/browse-list.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-'use client'
-
-import React from 'react'
-import { debounce } from 'lodash'
-import type { Thread } from '@repo/mb-genql'
-import { useBrowse } from '@/hooks/use-browse'
-import { getBrowseThreads } from '@/services/hasura'
-import BrowseListItem from './browse-list-item'
-
-const PAGE_SIZE = 50
-
-export default function BrowseList({ initialThreads }: BrowseListProps) {
- const { keyword, tab } = useBrowse()
- const [threads, setThreads] = React.useState(initialThreads)
- const [filteredThreads, setFilteredThreads] =
- React.useState(initialThreads)
- const [loading, setLoading] = React.useState(false)
- const [count, setCount] = React.useState(initialThreads.length)
-
- const verifyKeyword = () => {
- if (!keyword) {
- setFilteredThreads(threads)
- } else {
- debounce(() => {
- // TODO: Improve thread messages architecture to implement dynamic search to show only the thread title (first message on thread)
- // fetchThreads(keyword, tab)
- setFilteredThreads(
- threads.filter((thread: Thread) =>
- thread.messages[0]?.content
- .toLowerCase()
- .includes(keyword.toLowerCase())
- )
- )
- // ? Average time of human reaction is 230ms
- }, 230)()
- }
- }
-
- const loadMore = async () => {
- console.log('🟡 Loading More Content')
- setLoading(true)
-
- const moreThreads = await getBrowseThreads({
- categoryId: tab,
- offset: threads.length,
- limit: PAGE_SIZE
- })
-
- setThreads(prevState => [...prevState, ...moreThreads])
- setCount(moreThreads.length)
- setLoading(false)
- }
-
- React.useEffect(() => {
- verifyKeyword()
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [keyword, threads])
-
- return (
-
- {filteredThreads.map((thread: Thread, key) => (
-
- ))}
-
- )
-}
-
-type BrowseListProps = {
- initialThreads: Thread[]
-}
diff --git a/apps/masterbots.ai/components/browse/browse-specific-thread-list.tsx b/apps/masterbots.ai/components/browse/browse-specific-thread-list.tsx
deleted file mode 100644
index 4cbf613e..00000000
--- a/apps/masterbots.ai/components/browse/browse-specific-thread-list.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-'use client'
-
-import React from 'react'
-import type { Thread } from '@repo/mb-genql'
-import { getBrowseThreads } from '@/services/hasura'
-import BrowseListItem from './browse-list-item'
-
-export default function BrowseSpecificThreadList({
- initialThreads,
- query,
- PAGE_SIZE,
- pageType = ''
-}: {
- query: Record
- initialThreads: Thread[]
- PAGE_SIZE: number
- pageType?: string
-}) {
- const [threads, setThreads] = React.useState(initialThreads)
- const [loading, setLoading] = React.useState(false)
- const [count, setCount] = React.useState(initialThreads.length)
-
- const loadMore = async () => {
- console.log('🟡 Loading More Content')
- setLoading(true)
-
- const moreThreads = await getBrowseThreads({
- ...query,
- limit: PAGE_SIZE,
- offset: threads.length
- })
-
- setThreads(prevState => [...prevState, ...moreThreads])
- setCount(moreThreads.length)
- setLoading(false)
- }
-
- return (
-
- {threads.map((thread: Thread, key) => (
-
- ))}
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/browse-thread.tsx b/apps/masterbots.ai/components/browse/browse-thread.tsx
deleted file mode 100644
index aa010ad7..00000000
--- a/apps/masterbots.ai/components/browse/browse-thread.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-'use client'
-
-import type { Thread } from '@repo/mb-genql'
-import { cn } from '@/lib/utils'
-import { BrowseChatMessages } from './browse-chat-messages'
-
-export function BrowseThread({
- thread,
- className
-}: {
- thread: Thread
- className?: string
-}) {
- // we merge past assistant and user messages for ui only
- // we remove system prompts from ui
- // we extend append function to add our system prompts
-
- return (
-
- {thread.messages.length ? (
-
- ) : (
- ''
- )}
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/browse-user-details.tsx b/apps/masterbots.ai/components/browse/browse-user-details.tsx
deleted file mode 100644
index bb848475..00000000
--- a/apps/masterbots.ai/components/browse/browse-user-details.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-'use client'
-
-import type { User } from '@repo/mb-genql'
-import Image from 'next/image'
-import { useEffect, useState } from 'react'
-import { getBrowseThreads } from '@/services/hasura'
-import { Separator } from '../ui/separator'
-
-export default function BrowseChatbotDetails({ user }: { user?: User | null }) {
- const [threadNum, setThreadNum] = useState(0)
- const getThreadByUserName = async () => {
- const threads = await getBrowseThreads({
- slug: user.slug
- })
- setThreadNum(threads.length)
- }
- useEffect(() => {
- getThreadByUserName()
- }, [])
- return (
-
-
-
-
- {user.username.replace('_', ' ')}
-
-
-
-
-
- Threads: {threadNum ?? 1}
-
-
-
-
-
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/browse/shortlink-button.tsx b/apps/masterbots.ai/components/browse/shortlink-button.tsx
deleted file mode 100644
index 03585e9e..00000000
--- a/apps/masterbots.ai/components/browse/shortlink-button.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-'use client'
-
-import Link from 'next/link'
-import { useFormState, useFormStatus } from 'react-dom'
-import { CardContent } from '@/components/ui/card'
-import { Label } from '@/components/ui/label'
-import { Input } from '@/components/ui/input'
-import { Button } from '@/components/ui/button'
-import { shorten } from '@/app/actions'
-
-const initialState = {
- shortLink: ''
-}
-
-export default function Shortlink() {
- const [state, formAction] = useFormState(shorten, initialState)
-
- const url = window.location.href.replace(
- 'http://localhost:3000',
- 'https://dev.masterbots.ai'
- )
-
- return (
-
-
- {state.shortLink ? (
-
-
-
- {state.shortLink.replace(/^https?:\/\//, '')}
-
-
-
- ) : null}
-
- )
-}
-
-function SubmitButton() {
- const { pending } = useFormStatus()
-
- return (
-
- {pending ? : 'copy shortlink'}
-
- )
-}
-
-function LoadingCircle() {
- return (
-
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/c/thread-date-range-picker.tsx b/apps/masterbots.ai/components/c/thread-date-range-picker.tsx
deleted file mode 100644
index 26abf56e..00000000
--- a/apps/masterbots.ai/components/c/thread-date-range-picker.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-'use client'
-
-import * as React from 'react'
-import { CalendarIcon } from '@radix-ui/react-icons'
-import { addDays, format } from 'date-fns'
-import type { DateRange } from 'react-day-picker'
-import { cn } from '@/lib/utils'
-import { Button } from '@/components/ui/button'
-import { Calendar } from '@/components/ui/calendar'
-import {
- Popover,
- PopoverContent,
- PopoverTrigger
-} from '@/components/ui/popover'
-
-export function DateRangePicker({
- className
-}: React.HTMLAttributes) {
- const [date, setDate] = React.useState({
- from: new Date(2023, 0, 20),
- to: addDays(new Date(2023, 0, 20), 20)
- })
-
- return (
-
-
-
-
-
- {date.from ? (
- date.to ? (
- <>
- {format(date.from, 'LLL dd, y')} -{' '}
- {format(date.to, 'LLL dd, y')}
- >
- ) : (
- format(date.from, 'LLL dd, y')
- )
- ) : (
- Pick a date
- )}
-
-
-
-
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/empty-screen.tsx b/apps/masterbots.ai/components/empty-screen.tsx
deleted file mode 100644
index 73e63488..00000000
--- a/apps/masterbots.ai/components/empty-screen.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import type { UseChatHelpers } from 'ai/react'
-import { Button } from '@/components/ui/button'
-import { ExternalLink } from '@/components/external-link'
-import { IconArrowRight } from '@/components/ui/icons'
-import { botNames } from '@/lib/bots-names'
-
-const exampleMessages = [
- {
- heading: 'Explain technical concepts',
- message: `What is a "serverless function"?`
- },
- {
- heading: 'Summarize an article',
- message: 'Summarize the following article for a 2nd grader: \n'
- },
- {
- heading: 'Draft an email',
- message: `Draft an email to my boss about the following: \n`
- }
-]
-
-export function EmptyScreen({
- setInput,
- bot
-}: Pick & { bot: string }) {
- return (
-
-
-
- Welcome to Masterbots AI Chatbots! I{`'`}m {botNames.get(bot)}.
-
-
- You can start a conversation with me here or try the following
- examples:
-
-
- {exampleMessages.map((message, index) => (
- {
- setInput(message.message)
- }}
- variant="link"
- >
-
- {message.heading}
-
- ))}
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/external-link.tsx b/apps/masterbots.ai/components/external-link.tsx
deleted file mode 100644
index e5a1baee..00000000
--- a/apps/masterbots.ai/components/external-link.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-export function ExternalLink({
- href,
- children
-}: {
- href: string
- children: React.ReactNode
-}) {
- return (
-
- {children}
-
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/layout/header.tsx b/apps/masterbots.ai/components/layout/header.tsx
index 513c977a..2ed83d03 100644
--- a/apps/masterbots.ai/components/layout/header.tsx
+++ b/apps/masterbots.ai/components/layout/header.tsx
@@ -4,9 +4,9 @@ import { isTokenExpired } from '@repo/mb-lib'
import { cookies } from 'next/headers'
import { Button } from '@/components/ui/button'
import { IconSeparator } from '@/components/ui/icons'
-import { UserMenu } from '@/components/user-menu'
+import { UserMenu } from '@/components/layout/user-menu'
import { getUserProfile } from '@/services/supabase'
-import { SidebarToggle } from '../c/sidebar/sidebar-toggle'
+import { SidebarToggle } from '../routes/c/sidebar/sidebar-toggle'
// https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating
@@ -15,22 +15,24 @@ export async function Header() {
const jwt = cookies().get('hasuraJwt')?.value || ''
return (
-
-
-
-
-
-
-
-
-
- {user && !isTokenExpired(jwt) ? (
-
- ) : (
-
- Login
-
- )}
+
)
diff --git a/apps/masterbots.ai/components/layout/providers.tsx b/apps/masterbots.ai/components/layout/providers.tsx
index 8361f35d..09e35119 100644
--- a/apps/masterbots.ai/components/layout/providers.tsx
+++ b/apps/masterbots.ai/components/layout/providers.tsx
@@ -6,15 +6,20 @@ import type { ThemeProviderProps } from 'next-themes/dist/types'
import { SidebarProvider } from '@/hooks/use-sidebar'
import { TooltipProvider } from '@/components/ui/tooltip'
import { ThreadProvider } from '@/hooks/use-thread'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+
+const queryClient = new QueryClient()
export function Providers({ children, ...props }: ThemeProviderProps) {
return (
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
)
}
diff --git a/apps/masterbots.ai/components/tailwind-indicator.tsx b/apps/masterbots.ai/components/layout/tailwind-indicator.tsx
similarity index 100%
rename from apps/masterbots.ai/components/tailwind-indicator.tsx
rename to apps/masterbots.ai/components/layout/tailwind-indicator.tsx
diff --git a/apps/masterbots.ai/components/theme-toggle.tsx b/apps/masterbots.ai/components/layout/theme-toggle.tsx
similarity index 100%
rename from apps/masterbots.ai/components/theme-toggle.tsx
rename to apps/masterbots.ai/components/layout/theme-toggle.tsx
diff --git a/apps/masterbots.ai/components/user-menu.tsx b/apps/masterbots.ai/components/layout/user-menu.tsx
similarity index 100%
rename from apps/masterbots.ai/components/user-menu.tsx
rename to apps/masterbots.ai/components/layout/user-menu.tsx
diff --git a/apps/masterbots.ai/components/modal-coming-soon.tsx b/apps/masterbots.ai/components/modal-coming-soon.tsx
deleted file mode 100644
index 6da04058..00000000
--- a/apps/masterbots.ai/components/modal-coming-soon.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-'use client'
-
-import * as React from 'react'
-import { cn } from '@/lib/utils'
-import { IconClose } from './ui/icons'
-
-export interface ModalComingSoonProps extends React.ComponentProps<'div'> {
- isOpen: boolean
- onClose: () => void
-}
-
-export function ModalComingSoon({
- className,
- children,
- isOpen,
- onClose
-}: ModalComingSoonProps) {
- return (
-
-
-
-
-
-
-
-
-
- Coming Soon!
-
-
-
- )
-}
diff --git a/apps/masterbots.ai/components/button-scroll-to-bottom.tsx b/apps/masterbots.ai/components/routes/c/button-scroll-to-bottom.tsx
similarity index 100%
rename from apps/masterbots.ai/components/button-scroll-to-bottom.tsx
rename to apps/masterbots.ai/components/routes/c/button-scroll-to-bottom.tsx
diff --git a/apps/masterbots.ai/components/shared/chat-accordion.tsx b/apps/masterbots.ai/components/routes/c/chat-accordion.tsx
similarity index 100%
rename from apps/masterbots.ai/components/shared/chat-accordion.tsx
rename to apps/masterbots.ai/components/routes/c/chat-accordion.tsx
diff --git a/apps/masterbots.ai/components/c/chat-chatbot-details.tsx b/apps/masterbots.ai/components/routes/c/chat-chatbot-details.tsx
similarity index 98%
rename from apps/masterbots.ai/components/c/chat-chatbot-details.tsx
rename to apps/masterbots.ai/components/routes/c/chat-chatbot-details.tsx
index bb71adca..f1c47d38 100644
--- a/apps/masterbots.ai/components/c/chat-chatbot-details.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-chatbot-details.tsx
@@ -6,7 +6,7 @@ import { useSidebar } from '@/hooks/use-sidebar'
import { getCategory, getThreads } from '@/services/hasura'
import { useThread } from '@/hooks/use-thread'
import { useGlobalStore } from '@/hooks/use-global-store'
-import { Separator } from '../ui/separator'
+import { Separator } from '../../ui/separator'
export default function ChatChatbotDetails() {
const { user, hasuraJwt } = useGlobalStore()
diff --git a/apps/masterbots.ai/components/c/chat-chatbot.tsx b/apps/masterbots.ai/components/routes/c/chat-chatbot.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-chatbot.tsx
rename to apps/masterbots.ai/components/routes/c/chat-chatbot.tsx
diff --git a/apps/masterbots.ai/components/c/chat-clickable-text.tsx b/apps/masterbots.ai/components/routes/c/chat-clickable-text.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-clickable-text.tsx
rename to apps/masterbots.ai/components/routes/c/chat-clickable-text.tsx
diff --git a/apps/masterbots.ai/components/c/chat-history.tsx b/apps/masterbots.ai/components/routes/c/chat-history.tsx
similarity index 94%
rename from apps/masterbots.ai/components/c/chat-history.tsx
rename to apps/masterbots.ai/components/routes/c/chat-history.tsx
index 5c2a22c7..225dfc34 100644
--- a/apps/masterbots.ai/components/c/chat-history.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-history.tsx
@@ -1,7 +1,7 @@
import * as React from 'react'
import Link from 'next/link'
import { cn } from '@/lib/utils'
-import { SidebarList } from '@/components/c/sidebar/sidebar-list'
+import { SidebarList } from '@/components/routes/c/sidebar/sidebar-list'
import { buttonVariants } from '@/components/ui/button'
import { IconPlus } from '@/components/ui/icons'
diff --git a/apps/masterbots.ai/components/c/chat-layout-section.tsx b/apps/masterbots.ai/components/routes/c/chat-layout-section.tsx
similarity index 92%
rename from apps/masterbots.ai/components/c/chat-layout-section.tsx
rename to apps/masterbots.ai/components/routes/c/chat-layout-section.tsx
index 7abb0225..0b062805 100644
--- a/apps/masterbots.ai/components/c/chat-layout-section.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-layout-section.tsx
@@ -2,7 +2,7 @@
import * as React from 'react'
import { useThread } from '@/hooks/use-thread'
-import { ThreadPopup } from './thread-popup'
+import { ThreadPopup } from './chat-thread-popup'
export function ChatLayoutSection({ children }: { children: React.ReactNode }) {
const { sectionRef, isOpenPopup } = useThread()
diff --git a/apps/masterbots.ai/components/c/chat-list.tsx b/apps/masterbots.ai/components/routes/c/chat-list.tsx
similarity index 87%
rename from apps/masterbots.ai/components/c/chat-list.tsx
rename to apps/masterbots.ai/components/routes/c/chat-list.tsx
index bd09ec73..ff94d467 100644
--- a/apps/masterbots.ai/components/c/chat-list.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-list.tsx
@@ -1,11 +1,12 @@
import { type Message } from 'ai'
import type { Chatbot } from '@repo/mb-genql'
import React from 'react'
-import { ChatMessage } from '@/components/c/chat-message'
-import { cn, createMessagePairs } from '@/lib/utils'
+import { ChatMessage } from '@/components/routes/c/chat-message'
+import { cn } from '@/lib/utils'
import { useThread } from '@/hooks/use-thread'
-import { ShortMessage } from '../short-message'
-import { ChatAccordion } from '../shared/chat-accordion'
+import { ShortMessage } from '../../shared/thread-short-message'
+import { ChatAccordion } from './chat-accordion'
+import { createMessagePairs } from '@/lib/threads'
export interface ChatList {
messages: Message[]
@@ -36,14 +37,14 @@ export function ChatList({
const [pairs, setPairs] = React.useState
([])
const { isNewResponse } = useThread()
- React.useEffect(() => {
- if (messages.length) {
- const prePairs: MessagePair[] = createMessagePairs(
- messages
- ) as MessagePair[]
- setPairs(prePairs)
- } else setPairs([])
- }, [messages])
+ // React.useEffect(() => {
+ // if (messages.length) {
+ // const prePairs: MessagePair[] = createMessagePairs(
+ // messages as Message[]
+ // ) as MessagePair[]
+ // setPairs(prePairs)
+ // } else setPairs([])
+ // }, [messages])
if (!messages.length) return null
return (
diff --git a/apps/masterbots.ai/components/c/chat-message-actions.tsx b/apps/masterbots.ai/components/routes/c/chat-message-actions.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-message-actions.tsx
rename to apps/masterbots.ai/components/routes/c/chat-message-actions.tsx
diff --git a/apps/masterbots.ai/components/c/chat-message.tsx b/apps/masterbots.ai/components/routes/c/chat-message.tsx
similarity index 91%
rename from apps/masterbots.ai/components/c/chat-message.tsx
rename to apps/masterbots.ai/components/routes/c/chat-message.tsx
index 05700d7e..c3e20772 100644
--- a/apps/masterbots.ai/components/c/chat-message.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-message.tsx
@@ -5,11 +5,12 @@ import type { Message } from 'ai'
import type { Chatbot } from '@repo/mb-genql'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
-import { ClickableText } from '@/components/c/chat-clickable-text'
-import { ChatMessageActions } from '@/components/c/chat-message-actions'
-import { MemoizedReactMarkdown } from '@/components/markdown'
+import { ClickableText } from '@/components/routes/c/chat-clickable-text'
+import { ChatMessageActions } from '@/components/routes/c/chat-message-actions'
+import { MemoizedReactMarkdown } from '@/components/shared/markdown'
import { CodeBlock } from '@/components/ui/codeblock'
-import { cleanPrompt, cn } from '@/lib/utils'
+import { cn } from '@/lib/utils'
+import { cleanPrompt } from '@/lib/threads'
export interface ChatMessageProps {
message: Message
diff --git a/apps/masterbots.ai/components/c/chat-panel.tsx b/apps/masterbots.ai/components/routes/c/chat-panel.tsx
similarity index 89%
rename from apps/masterbots.ai/components/c/chat-panel.tsx
rename to apps/masterbots.ai/components/routes/c/chat-panel.tsx
index cbaeb51d..1861b4b3 100644
--- a/apps/masterbots.ai/components/c/chat-panel.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-panel.tsx
@@ -2,11 +2,11 @@ import * as React from 'react'
import { type UseChatHelpers } from 'ai/react'
import { Chatbot } from '@repo/mb-genql'
import { Button } from '@/components/ui/button'
-import { PromptForm } from '@/components/c/prompt-form'
-import { ButtonScrollToBottom } from '@/components/button-scroll-to-bottom'
+import { PromptForm } from '@/components/routes/c/prompt-form'
+import { ButtonScrollToBottom } from '@/components/routes/c/button-scroll-to-bottom'
import { IconRefresh, IconShare, IconStop } from '@/components/ui/icons'
import { FooterText } from '@/components/layout/footer'
-import { ChatShareDialog } from '@/components/c/chat-share-dialog'
+import { ChatShareDialog } from '@/components/routes/c/chat-share-dialog'
import { cn } from '@/lib/utils'
import { useThread } from '@/hooks/use-thread'
@@ -67,7 +67,9 @@ export function ChatPanel({
{isLoading ? (
{ stop(); }}
+ onClick={() => {
+ stop()
+ }}
variant="outline"
>
@@ -83,7 +85,9 @@ export function ChatPanel({
{id && title ? (
<>
{ setShareDialogOpen(true); }}
+ onClick={() => {
+ setShareDialogOpen(true)
+ }}
variant="outline"
>
diff --git a/apps/masterbots.ai/components/c/chat-scroll-anchor.tsx b/apps/masterbots.ai/components/routes/c/chat-scroll-anchor.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-scroll-anchor.tsx
rename to apps/masterbots.ai/components/routes/c/chat-scroll-anchor.tsx
diff --git a/apps/masterbots.ai/components/c/chat-search-input.tsx b/apps/masterbots.ai/components/routes/c/chat-search-input.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-search-input.tsx
rename to apps/masterbots.ai/components/routes/c/chat-search-input.tsx
diff --git a/apps/masterbots.ai/components/c/chat-share-dialog.tsx b/apps/masterbots.ai/components/routes/c/chat-share-dialog.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-share-dialog.tsx
rename to apps/masterbots.ai/components/routes/c/chat-share-dialog.tsx
diff --git a/apps/masterbots.ai/components/c/chat-thread-list-panel.tsx b/apps/masterbots.ai/components/routes/c/chat-thread-list-panel.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/chat-thread-list-panel.tsx
rename to apps/masterbots.ai/components/routes/c/chat-thread-list-panel.tsx
diff --git a/apps/masterbots.ai/components/c/thread-list.tsx b/apps/masterbots.ai/components/routes/c/chat-thread-list.tsx
similarity index 97%
rename from apps/masterbots.ai/components/c/thread-list.tsx
rename to apps/masterbots.ai/components/routes/c/chat-thread-list.tsx
index 39308732..b8ac8382 100644
--- a/apps/masterbots.ai/components/c/thread-list.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-thread-list.tsx
@@ -6,8 +6,8 @@ import React from 'react'
import { useThread } from '@/hooks/use-thread'
import { useSidebar } from '@/hooks/use-sidebar'
import { cn, sleep } from '@/lib/utils'
-import { ShortMessage } from '../short-message'
-import { ChatAccordion } from '../shared/chat-accordion'
+import { ShortMessage } from '../../shared/thread-short-message'
+import { ChatAccordion } from './chat-accordion'
import { ChatList } from './chat-list'
export default function ThreadList({
diff --git a/apps/masterbots.ai/components/c/thread-popup.tsx b/apps/masterbots.ai/components/routes/c/chat-thread-popup.tsx
similarity index 96%
rename from apps/masterbots.ai/components/c/thread-popup.tsx
rename to apps/masterbots.ai/components/routes/c/chat-thread-popup.tsx
index c090ae60..55bf68fd 100644
--- a/apps/masterbots.ai/components/c/thread-popup.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat-thread-popup.tsx
@@ -3,11 +3,12 @@
import { useEffect, useRef } from 'react'
import { useScroll } from 'framer-motion'
import { useThread } from '@/hooks/use-thread'
-import { cn, scrollToBottomOfElement } from '@/lib/utils'
+import { cn } from '@/lib/utils'
import { useAtBottom } from '@/hooks/use-at-bottom'
-import { IconClose } from '../ui/icons'
+import { IconClose } from '../../ui/icons'
import { Chat } from './chat'
import { ChatList } from './chat-list'
+import { scrollToBottomOfElement } from '@/lib/animation'
export function ThreadPopup({ className }: { className?: string }) {
const {
diff --git a/apps/masterbots.ai/components/c/chat.tsx b/apps/masterbots.ai/components/routes/c/chat.tsx
similarity index 96%
rename from apps/masterbots.ai/components/c/chat.tsx
rename to apps/masterbots.ai/components/routes/c/chat.tsx
index e8ccc0ad..b3d6b801 100644
--- a/apps/masterbots.ai/components/c/chat.tsx
+++ b/apps/masterbots.ai/components/routes/c/chat.tsx
@@ -9,16 +9,17 @@ import type { Chatbot } from '@repo/mb-genql'
import { useParams } from 'next/navigation'
import React, { useEffect } from 'react'
import { toast } from 'react-hot-toast'
-import { ChatList } from '@/components/c/chat-list'
-import { ChatPanel } from '@/components/c/chat-panel'
-import { ChatScrollAnchor } from '@/components/c/chat-scroll-anchor'
-import { cn, extractBetweenMarkers, scrollToBottomOfElement } from '@/lib/utils'
+import { ChatList } from '@/components/routes/c/chat-list'
+import { ChatPanel } from '@/components/routes/c/chat-panel'
+import { ChatScrollAnchor } from '@/components/routes/c/chat-scroll-anchor'
+import { cn, extractBetweenMarkers } from '@/lib/utils'
import { useAtBottom } from '@/hooks/use-at-bottom'
import { createThread, getThread, saveNewMessage } from '@/services/hasura'
import { useThread } from '@/hooks/use-thread'
import { botNames } from '@/lib/bots-names'
import { useSidebar } from '@/hooks/use-sidebar'
import { useGlobalStore } from '@/hooks/use-global-store'
+import { scrollToBottomOfElement } from '@/lib/animation'
export function Chat({
initialMessages,
diff --git a/apps/masterbots.ai/components/c/new-chat.tsx b/apps/masterbots.ai/components/routes/c/new-chat.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/new-chat.tsx
rename to apps/masterbots.ai/components/routes/c/new-chat.tsx
diff --git a/apps/masterbots.ai/components/c/prompt-form.tsx b/apps/masterbots.ai/components/routes/c/prompt-form.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/prompt-form.tsx
rename to apps/masterbots.ai/components/routes/c/prompt-form.tsx
diff --git a/apps/masterbots.ai/components/clear-history.tsx b/apps/masterbots.ai/components/routes/c/sidebar/clear-history.tsx
similarity index 100%
rename from apps/masterbots.ai/components/clear-history.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/clear-history.tsx
diff --git a/apps/masterbots.ai/components/c/sidebar/index.tsx b/apps/masterbots.ai/components/routes/c/sidebar/index.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/sidebar/index.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/index.tsx
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-actions.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-actions.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-actions.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-actions.tsx
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-category-general.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-category-general.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-category-general.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-category-general.tsx
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-footer.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-footer.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-footer.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-footer.tsx
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-item.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-item.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-item.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-item.tsx
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-items.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-items.tsx
similarity index 90%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-items.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-items.tsx
index c4491bd8..714b6c82 100644
--- a/apps/masterbots.ai/components/c/sidebar/sidebar-items.tsx
+++ b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-items.tsx
@@ -2,7 +2,7 @@
import { AnimatePresence, motion } from 'framer-motion'
import type { Chat } from '@/lib/types'
-import { SidebarItem } from '@/components/c/sidebar/sidebar-item'
+import { SidebarItem } from '@/components/routes/c/sidebar/sidebar-item'
interface SidebarItemsProps {
chats?: Chat[]
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-link.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-link.tsx
similarity index 99%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-link.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-link.tsx
index c6407316..46d2a4af 100644
--- a/apps/masterbots.ai/components/c/sidebar/sidebar-link.tsx
+++ b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-link.tsx
@@ -8,7 +8,7 @@ import React from 'react'
import { getChatbots } from '@/services/hasura'
import { cn } from '@/lib/utils'
import { useSidebar } from '@/hooks/use-sidebar'
-import { IconCaretRight } from '../../ui/icons'
+import { IconCaretRight } from '../../../ui/icons'
const PAGE_SIZE = 20
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-list.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-list.tsx
similarity index 83%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-list.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-list.tsx
index b1881ebd..f8c45c66 100644
--- a/apps/masterbots.ai/components/c/sidebar/sidebar-list.tsx
+++ b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-list.tsx
@@ -1,7 +1,6 @@
import { cache } from 'react'
-import { ClearHistory } from '@/components/clear-history'
-import { SidebarItems } from '@/components/c/sidebar/sidebar-items'
-import { ThemeToggle } from '@/components/theme-toggle'
+import { SidebarItems } from '@/components/routes/c/sidebar/sidebar-items'
+import { ThemeToggle } from '@/components/layout/theme-toggle'
interface SidebarListProps {
userId?: string
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-mobile.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-mobile.tsx
similarity index 93%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-mobile.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-mobile.tsx
index ebff107d..eacfa285 100644
--- a/apps/masterbots.ai/components/c/sidebar/sidebar-mobile.tsx
+++ b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-mobile.tsx
@@ -1,7 +1,7 @@
'use client'
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
-import { Sidebar } from '@/components/c/sidebar'
+import { Sidebar } from '@/components/routes/c/sidebar'
import { Button } from '@/components/ui/button'
import { IconSidebar } from '@/components/ui/icons'
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-responsive.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-responsive.tsx
similarity index 91%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-responsive.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-responsive.tsx
index 0252dfcd..7f0ae2b9 100644
--- a/apps/masterbots.ai/components/c/sidebar/sidebar-responsive.tsx
+++ b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-responsive.tsx
@@ -1,4 +1,4 @@
-import { Sidebar } from '@/components/c/sidebar'
+import { Sidebar } from '@/components/routes/c/sidebar'
// import { ChatHistory } from '@/components/chat-history'
import { SidebarGeneralCategory } from './sidebar-category-general'
diff --git a/apps/masterbots.ai/components/c/sidebar/sidebar-toggle.tsx b/apps/masterbots.ai/components/routes/c/sidebar/sidebar-toggle.tsx
similarity index 100%
rename from apps/masterbots.ai/components/c/sidebar/sidebar-toggle.tsx
rename to apps/masterbots.ai/components/routes/c/sidebar/sidebar-toggle.tsx
diff --git a/apps/masterbots.ai/components/c/thread-panel/index.tsx b/apps/masterbots.ai/components/routes/c/thread-panel/index.tsx
similarity index 77%
rename from apps/masterbots.ai/components/c/thread-panel/index.tsx
rename to apps/masterbots.ai/components/routes/c/thread-panel/index.tsx
index db5f9b93..eacc3ff3 100644
--- a/apps/masterbots.ai/components/c/thread-panel/index.tsx
+++ b/apps/masterbots.ai/components/routes/c/thread-panel/index.tsx
@@ -1,5 +1,5 @@
import type { Thread } from '@repo/mb-genql'
-import UserThreadPanel from '@/components/c/thread-panel/user-thread-panel'
+import UserThreadPanel from '@/components/routes/c/thread-panel/user-thread-panel'
export default async function ThreadPanel({
chatbot,
diff --git a/apps/masterbots.ai/components/c/thread-panel/user-thread-panel.tsx b/apps/masterbots.ai/components/routes/c/thread-panel/user-thread-panel.tsx
similarity index 96%
rename from apps/masterbots.ai/components/c/thread-panel/user-thread-panel.tsx
rename to apps/masterbots.ai/components/routes/c/thread-panel/user-thread-panel.tsx
index f3eee1d7..e6a8e32f 100644
--- a/apps/masterbots.ai/components/c/thread-panel/user-thread-panel.tsx
+++ b/apps/masterbots.ai/components/routes/c/thread-panel/user-thread-panel.tsx
@@ -2,13 +2,14 @@
import type { Thread } from '@repo/mb-genql'
import React, { useEffect, useRef, useState } from 'react'
-import { ChatSearchInput } from '@/components/c/chat-search-input'
-import ThreadList from '@/components/c/thread-list'
+import { ChatSearchInput } from '@/components/routes/c/chat-search-input'
+
import { useSidebar } from '@/hooks/use-sidebar'
import { useThread } from '@/hooks/use-thread'
import { getThreads } from '@/services/hasura'
import { useGlobalStore } from '@/hooks/use-global-store'
import ChatChatbotDetails from '../chat-chatbot-details'
+import ThreadList from '../chat-thread-list'
const PAGE_SIZE = 20
diff --git a/apps/masterbots.ai/components/p/early-access-from.tsx b/apps/masterbots.ai/components/routes/p/early-access-from.tsx
similarity index 96%
rename from apps/masterbots.ai/components/p/early-access-from.tsx
rename to apps/masterbots.ai/components/routes/p/early-access-from.tsx
index cb71bd58..1ab55f8d 100644
--- a/apps/masterbots.ai/components/p/early-access-from.tsx
+++ b/apps/masterbots.ai/components/routes/p/early-access-from.tsx
@@ -6,7 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
-import { Input } from '../ui/input'
+import { Input } from '../../ui/input'
const schema = z.object({
otherText: z.string().min(1).optional(),
@@ -87,7 +87,9 @@ export async function WorkEarlyAccessForm() {
)}
{formState.errors.otherText ? Please specify other interest
: null}
- {formState.isSubmitted && formState.errors && !formState.isValid ? Please select at least one option
: null}
+ {formState.isSubmitted && formState.errors && !formState.isValid ? (
+ Please select at least one option
+ ) : null}
Get early access
diff --git a/apps/masterbots.ai/components/shared/account-avatar.tsx b/apps/masterbots.ai/components/shared/account-avatar.tsx
new file mode 100644
index 00000000..6378bdda
--- /dev/null
+++ b/apps/masterbots.ai/components/shared/account-avatar.tsx
@@ -0,0 +1,35 @@
+import { cn } from '@/lib/utils'
+import Image from 'next/image'
+import Link from 'next/link'
+import { IconUser } from '../ui/icons'
+
+export function AccountAvatar({ href, alt, src, size = 32 }: MbAvatarProp) {
+ return (
+
+ {src ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+interface MbAvatarProp {
+ href: string
+ alt: string
+ src?: string
+ size?: number
+}
diff --git a/apps/masterbots.ai/components/shared/account-details.tsx b/apps/masterbots.ai/components/shared/account-details.tsx
new file mode 100644
index 00000000..6e3c6b61
--- /dev/null
+++ b/apps/masterbots.ai/components/shared/account-details.tsx
@@ -0,0 +1,72 @@
+import Image from 'next/image'
+import Link from 'next/link'
+import { Separator } from '../ui/separator'
+
+export default function AccountDetails({
+ alt,
+ avatar,
+ username,
+ href,
+ threadNum = 0,
+ chatbotName,
+ description
+}: AccountDetailsProps) {
+ if (!username && !chatbotName)
+ throw new Error('You must pass username or chatbotName')
+
+ return (
+
+
+
+
+
+
+
+
{username || chatbotName}
+
+ {/*
+ {chatbot.categories[0].category.name}.
+
*/}
+
+
+ {description ?
{description}
: ''}
+
+
+
+
+
+ Threads: {threadNum ?? 0}
+
+
+ {chatbotName ? (
+
+ Chat with {chatbotName} >
+
+ ) : null}
+
+
+
+
+
+ )
+}
+
+interface AccountDetailsProps {
+ alt?: string
+ avatar: string
+ username?: string
+ href: string
+ threadNum?: number
+ chatbotName?: string
+ description?: string
+}
diff --git a/apps/masterbots.ai/components/browse/browse-search-input.tsx b/apps/masterbots.ai/components/shared/browse-input.tsx
similarity index 97%
rename from apps/masterbots.ai/components/browse/browse-search-input.tsx
rename to apps/masterbots.ai/components/shared/browse-input.tsx
index 414f3fb1..6bb79d55 100644
--- a/apps/masterbots.ai/components/browse/browse-search-input.tsx
+++ b/apps/masterbots.ai/components/shared/browse-input.tsx
@@ -6,7 +6,7 @@ import { IconClose } from '@/components/ui/icons'
import { Input } from '@/components/ui/input'
import { useBrowse } from '@/hooks/use-browse'
-export function BrowseSearchInput() {
+export function BrowseInput() {
const { keyword, changeKeyword } = useBrowse()
return (
diff --git a/apps/masterbots.ai/components/browse/browse-category-link.tsx b/apps/masterbots.ai/components/shared/category-tabs/category-link.tsx
similarity index 95%
rename from apps/masterbots.ai/components/browse/browse-category-link.tsx
rename to apps/masterbots.ai/components/shared/category-tabs/category-link.tsx
index c4b1b4fa..f5969b4a 100644
--- a/apps/masterbots.ai/components/browse/browse-category-link.tsx
+++ b/apps/masterbots.ai/components/shared/category-tabs/category-link.tsx
@@ -2,7 +2,7 @@ import { motion } from 'framer-motion'
import type { Category } from '@repo/mb-genql'
import Link from 'next/link'
-export function BrowseCategoryLink({
+export function CategoryLink({
category,
activeTab,
onClick,
@@ -24,7 +24,7 @@ export function BrowseCategoryLink({
href={
category === 'all'
? '/'
- : `/${category.name.toLowerCase().replace(/\s+/g, '_').replace(/\&/g, 'n')}`
+ : `/${category.name.toLowerCase().replace(/\s+/g, '_').replace(/\&/g, '_')}`
}
id={id}
onClick={onClick}
diff --git a/apps/masterbots.ai/components/browse/browse-category-tabs.tsx b/apps/masterbots.ai/components/shared/category-tabs/category-tabs.tsx
similarity index 90%
rename from apps/masterbots.ai/components/browse/browse-category-tabs.tsx
rename to apps/masterbots.ai/components/shared/category-tabs/category-tabs.tsx
index 9f212b6f..0c7c8d6f 100644
--- a/apps/masterbots.ai/components/browse/browse-category-tabs.tsx
+++ b/apps/masterbots.ai/components/shared/category-tabs/category-tabs.tsx
@@ -3,9 +3,9 @@
import type { Category } from '@repo/mb-genql'
import { useEffect } from 'react'
import { useBrowse } from '@/hooks/use-browse'
-import { BrowseCategoryLink } from './browse-category-link'
+import { CategoryLink } from './category-link'
-export function BrowseCategoryTabs({
+export function CategoryTabs({
categories,
initialCategory = 'all'
}: {
@@ -36,7 +36,7 @@ export function BrowseCategoryTabs({
setActiveTab(
categories.filter(
c =>
- c.name.toLowerCase().replace(/\s+/g, '_').replace(/\&/g, 'n') ===
+ c.name.toLowerCase().replace(/\s+/g, '_').replace(/\&/g, '_') ===
initialCategory
)[0]?.categoryId
)
@@ -46,7 +46,7 @@ export function BrowseCategoryTabs({
return (
-
{categories.map((category, key) => (
-
{
+ // Stop propagation to prevent form submission when clicking on the icon
+ e.preventDefault()
+ e.stopPropagation()
+ const formData = new FormData()
+ formData.set('url', url)
+ const { shortLink } = await shorten({}, formData)
+ navigator.clipboard
+ .writeText(shortLink)
+ .then(() => console.log('Shortlink copied to clipboard'))
+ .catch(error =>
+ console.error('Error copying shortlink to clipboard: ', error)
+ )
+ }
+
+ return
+}
diff --git a/apps/masterbots.ai/components/markdown.tsx b/apps/masterbots.ai/components/shared/markdown.tsx
similarity index 100%
rename from apps/masterbots.ai/components/markdown.tsx
rename to apps/masterbots.ai/components/shared/markdown.tsx
diff --git a/apps/masterbots.ai/components/shared/thread-accordion.tsx b/apps/masterbots.ai/components/shared/thread-accordion.tsx
new file mode 100644
index 00000000..74d6f0ba
--- /dev/null
+++ b/apps/masterbots.ai/components/shared/thread-accordion.tsx
@@ -0,0 +1,95 @@
+'use client'
+
+import { useEffect } from 'react'
+
+import { getMessagePairs } from '@/services/hasura'
+
+import { useQuery } from '@tanstack/react-query'
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger
+} from '@/components/ui/accordion'
+
+import { Thread } from '@repo/mb-genql'
+import { ThreadHeading } from './thread-heading'
+import { MessagePair, convertMessage } from '@/lib/threads'
+import { BrowseChatMessage } from './thread-message'
+
+export function ThreadAccordion({
+ thread,
+ initialMessagePairs,
+ clientFetch = false
+}: ThreadAccordionProps) {
+ // initalMessages is coming from server ssr on load. the rest of messages on demand on mount
+ const { data: pairs, error } = useQuery({
+ queryKey: [`messages-${thread.threadId}`],
+ queryFn: () => getMessagePairs(thread.threadId),
+ initialData: initialMessagePairs,
+ refetchOnMount: true,
+ enabled: clientFetch
+ })
+
+ // update url when dialog opens and closes
+ useEffect(() => {
+ const initialUrl = location.href
+ const threadUrl = `/${thread.chatbot.categories[0].category.name.toLowerCase().replaceAll(' ', '_').replaceAll('&', '_')}/${thread.threadId}`
+ console.log(`Updating URL to ${threadUrl}, initialUrl was ${initialUrl}`)
+
+ window.history.pushState({}, '', threadUrl)
+ return () => {
+ window.history.pushState({}, '', initialUrl)
+ }
+ })
+
+ if (error) return There was an error loading thread messages
+
+ // if no initial message and still loading show loading message
+ // NOTE: its fast and transitions in. testing without this
+ if (!pairs?.length) return null
+
+ console.log(pairs.map((_p, key) => `pair-${key}`))
+ return (
+
+ {pairs.map((p, key) => {
+ return (
+
+
+ {key ? (
+ {p.userMessage.content}
+ ) : (
+
+ )}
+
+
+
+ {p.chatGptMessage.map((message, index) => (
+
+ ))}
+
+
+
+ )
+ })}
+
+ )
+}
+
+interface ThreadAccordionProps {
+ thread: Thread
+ initialMessagePairs?: MessagePair[]
+ clientFetch?: boolean
+}
diff --git a/apps/masterbots.ai/components/shared/thread-dialog.tsx b/apps/masterbots.ai/components/shared/thread-dialog.tsx
new file mode 100644
index 00000000..cf0a5f6a
--- /dev/null
+++ b/apps/masterbots.ai/components/shared/thread-dialog.tsx
@@ -0,0 +1,38 @@
+'use client'
+
+import type { Thread } from '@repo/mb-genql'
+import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
+import { ThreadAccordion } from './thread-accordion'
+import { ThreadHeading } from './thread-heading'
+import { cn } from '@/lib/utils'
+
+export function ThreadDialog({ thread }: ThreadDialogProps) {
+ const firstQuestion =
+ thread.messages.find(m => m.role === 'user')?.content || 'not found'
+ const firstResponse =
+ thread.messages.find(m => m.role === 'assistant')?.content || 'not found'
+
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+interface ThreadDialogProps {
+ thread: Thread
+}
diff --git a/apps/masterbots.ai/components/shared/thread-heading.tsx b/apps/masterbots.ai/components/shared/thread-heading.tsx
new file mode 100644
index 00000000..604a3bc0
--- /dev/null
+++ b/apps/masterbots.ai/components/shared/thread-heading.tsx
@@ -0,0 +1,64 @@
+import { Thread } from '@repo/mb-genql'
+import { ShortMessage } from './thread-short-message'
+import { AccountAvatar } from './account-avatar'
+import { cn } from '@/lib/utils'
+import Shortlink from './copy-shortlink'
+
+export function ThreadHeading({
+ thread,
+ response,
+ question,
+ copy = false
+}: ThreadHeadingProps) {
+ return (
+
+
+
+
+
+
+ {question}
+
+
by
+
+
+
+
+ {copy ?
: null}
+
+
+ {response ? (
+
+
+
+ ) : null}
+
+ )
+}
+
+interface ThreadHeadingProps {
+ thread: Thread
+ response?: string
+ question: string
+ copy?: boolean
+}
diff --git a/apps/masterbots.ai/components/shared/thread-list.tsx b/apps/masterbots.ai/components/shared/thread-list.tsx
new file mode 100644
index 00000000..d5121b58
--- /dev/null
+++ b/apps/masterbots.ai/components/shared/thread-list.tsx
@@ -0,0 +1,91 @@
+'use client'
+
+import React, { useEffect, useRef, useState } from 'react'
+import { debounce } from 'lodash'
+import type { Thread } from '@repo/mb-genql'
+import { useBrowse } from '@/hooks/use-browse'
+import { getBrowseThreads } from '@/services/hasura'
+import { ThreadDialog } from './thread-dialog'
+
+export default function ThreadList({
+ initialThreads,
+ filter
+}: ThreadListProps) {
+ const { keyword } = useBrowse()
+ const [threads, setThreads] = useState(initialThreads)
+ const [filteredThreads, setFilteredThreads] =
+ useState(initialThreads)
+ const [loading, setLoading] = useState(false)
+ const loadMoreRef = useRef(null)
+ const [hasMore, setHasMore] = useState(true)
+
+ // load more threads for the category
+ const loadMore = async () => {
+ console.log('🟡 Loading More Content')
+ setLoading(true)
+
+ const moreThreads = await getBrowseThreads({
+ ...filter,
+ offset: filteredThreads.length,
+ limit: 50
+ })
+
+ if (moreThreads.length === 0) setHasMore(false)
+ setThreads(prevState => [...prevState, ...moreThreads])
+ setLoading(false)
+ }
+
+ const verifyKeyword = () => {
+ if (!keyword) {
+ setFilteredThreads(threads)
+ } else {
+ debounce(() => {
+ setFilteredThreads(
+ threads.filter((thread: Thread) =>
+ thread.messages[0]?.content
+ .toLowerCase()
+ .includes(keyword.toLowerCase())
+ )
+ )
+ }, 230)()
+ }
+ }
+
+ useEffect(() => {
+ verifyKeyword()
+ }, [keyword, threads, verifyKeyword])
+
+ // load mare item when it gets to the end
+ useEffect(() => {
+ if (!loadMoreRef.current) return
+ const observer = new IntersectionObserver(([entry]) => {
+ if (hasMore && entry.isIntersecting && !loading) {
+ setTimeout(() => loadMore(), 150)
+ observer.unobserve(entry.target)
+ }
+ })
+
+ observer.observe(loadMoreRef.current)
+
+ return () => observer.disconnect()
+ }, [hasMore, loading, loadMore])
+
+ return (
+
+ {filteredThreads.map((thread: Thread, key) => (
+
+ ))}
+
+
+ )
+}
+
+type ThreadListProps = {
+ initialThreads: Thread[]
+ filter: {
+ categoryId?: number
+ userId?: string
+ chatbotName?: string
+ slug?: string
+ }
+}
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/short-message.tsx b/apps/masterbots.ai/components/shared/thread-short-message.tsx
similarity index 97%
rename from apps/masterbots.ai/components/short-message.tsx
rename to apps/masterbots.ai/components/shared/thread-short-message.tsx
index 0288cee3..6ff0a501 100644
--- a/apps/masterbots.ai/components/short-message.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 { 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..0c4184a1 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({
@@ -506,7 +507,6 @@ export async function getUserInfoFromBrowse(slug: string) {
user: {
username: true,
profilePicture: true,
- name: true,
__args: {
where: {
slug: {
@@ -541,3 +541,8 @@ export async function getUser({
})
return user[0]
}
+
+export async function getMessagePairs(threadId) {
+ const messages = await getMessages({ threadId })
+ return createMessagePairs(messages)
+}
diff --git a/apps/masterbots.ai/services/supabase/supa-server.service.ts b/apps/masterbots.ai/services/supabase/supa-server.service.ts
index 4c61d863..b154df97 100644
--- a/apps/masterbots.ai/services/supabase/supa-server.service.ts
+++ b/apps/masterbots.ai/services/supabase/supa-server.service.ts
@@ -4,26 +4,27 @@ import { createSupabaseServerClient } from './supa-server-client'
export async function getUserProfile(): Promise {
try {
- const supabase = await createSupabaseServerClient()
- const {
- data: { user }
- } = await supabase.auth.getUser()
- if (!user || !user.email) throw new Error('user not found')
+ return null
+ // const supabase = await createSupabaseServerClient()
+ // const {
+ // data: { user }
+ // } = await supabase.auth.getUser()
+ // if (!user || !user.email) throw new Error('user not found')
- // TODO: use supabase
- const userProfile = await getUser({
- email: user.email,
- adminSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET || ''
- })
+ // // TODO: use supabase
+ // const userProfile = await getUser({
+ // email: user.email,
+ // adminSecret: process.env.HASURA_GRAPHQL_ADMIN_SECRET || ''
+ // })
- if (!userProfile) throw new Error('user not found')
- return {
- userId: userProfile.userId,
- username: userProfile.username,
- name: '',
- email: userProfile.email,
- image: userProfile.profilePicture || ''
- }
+ // if (!userProfile) throw new Error('user not found')
+ // return {
+ // userId: userProfile.userId,
+ // username: userProfile.username,
+ // name: '',
+ // email: userProfile.email,
+ // image: userProfile.profilePicture || ''
+ // }
} catch (error) {
console.log('GET USER PROFILE ERROR', error)
return null
diff --git a/bun.lockb b/bun.lockb
index d05fce61..ed82c55c 100755
Binary files a/bun.lockb and b/bun.lockb differ