Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/masterbots.ai/components/layout/sidebar/sidebar-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ const ChatbotComponent: React.FC<ChatbotComponentProps> = React.memo(
const { userSlug, domain } = useParams()
const { setIsOpenPopup, setActiveThread } = useThread()

const isPro = routeType === 'pro'
const canonicalDomain = getCanonicalDomain(chatbot.name)
// * Default to prompt when no metadata found... Special case for BlankBot
const chatbotDomain = canonicalDomain || (domain as string) || 'prompt'
Expand Down Expand Up @@ -274,7 +275,7 @@ const ChatbotComponent: React.FC<ChatbotComponentProps> = React.memo(
return navigateTo({
urlType: 'chatbotThreadListUrl',
navigationParams: {
type: isPublic ? 'public' : 'personal',
type: isPro ? 'pro' : isPublic ? 'public' : 'personal',
category: category.name,
domain: chatbotDomain,
chatbot: chatbot.name,
Expand Down Expand Up @@ -303,7 +304,7 @@ const ChatbotComponent: React.FC<ChatbotComponentProps> = React.memo(
chatbot: chatbot?.name,
})
: urlBuilders.chatbotThreadListUrl({
type: isPublic ? 'public' : 'personal',
type: isPro ? 'pro' : isPublic ? 'public' : 'personal',
category: category.name,
domain: chatbotDomain,
chatbot: chatbot?.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
*/

import { MemoizedReactMarkdown } from '@/components/shared/markdown'
import { CodeBlock } from '@/components/ui/codeblock'
import { cleanPrompt } from '@/lib/helpers/ai-helpers'
import { memoizedMarkdownComponents } from '@/lib/memoized-markdown-components'
import { cn } from '@/lib/utils'
import type { Message } from 'ai'
import type { Chatbot } from 'mb-genql'
Expand All @@ -51,65 +51,7 @@ export function BrowseChatMessage({
<MemoizedReactMarkdown
className="min-w-full prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 !max-w-5xl"
remarkPlugins={[remarkGfm, rehypeMathJax, remarkRehype]}
components={{
// @ts-ignore
p({ children }) {
return (
<p className="mb-2 whitespace-pre-line last:mb-0">{children}</p>
)
},
// @ts-ignore
ol({ children }) {
return (
<ol className="text-left list-decimal list-inside">
{children}
</ol>
)
},
// @ts-ignore
ul({ children }) {
return (
<ul className="text-left list-disc list-inside">{children}</ul>
)
},
// @ts-ignore
code({
node,
inline = false,
className,
children,
...props
}: React.HTMLAttributes<HTMLElement> & {
node: unknown
inline?: boolean
}) {
const childrenText = String(children)
if (childrenText?.startsWith('▍')) {
return (
<span className="mt-1 cursor-default animate-pulse">▍</span>
)
}

const match = /language-(\w+)/.exec(className || '')

if (inline) {
return (
<code className={className} {...props}>
{children}
</code>
)
}

return (
<CodeBlock
key={Math.random()}
language={match?.[1] || ''}
value={String(children).replace(/\n$/, '')}
{...props}
/>
)
},
}}
components={memoizedMarkdownComponents()}
>
{cleanMessage.content}
</MemoizedReactMarkdown>
Expand Down
189 changes: 6 additions & 183 deletions apps/masterbots.ai/components/routes/chat/chat-message.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,18 @@
import { ChatMessageActions } from '@/components/routes/chat/chat-message-actions'
import { MemoizedReactMarkdown } from '@/components/shared/markdown'
import { CodeBlock } from '@/components/ui/codeblock'
import {
cleanClickableText,
extractFollowUpContext,
getTextFromChildren,
} from '@/lib/chat-clickable-text'
import { cleanPrompt } from '@/lib/helpers/ai-helpers'
import { memoizedMarkdownComponents } from '@/lib/memoized-markdown-components'
import { cn } from '@/lib/utils'
import type { ChatMessageProps, WebSearchResult } from '@/types/types'
import React, { useState } from 'react'
import { useState } from 'react'
import rehypeMathJax from 'rehype-mathjax'
import remarkGfm from 'remark-gfm'
import remarkRehype from 'remark-rehype'

/**
* Preprocesses the children to combine adjacent nodes with a colon.
*
* @param children The children nodes.
* @returns The preprocessed children.
*/

const preprocessChildren = (children: React.ReactNode): React.ReactNode => {
if (!Array.isArray(children)) return children

const result: React.ReactNode[] = []
let i = 0

while (i < children.length) {
const current = children[i]
const next = i + 1 < children.length ? children[i + 1] : null

// If we detect a pattern where a node is immediately followed by ":"
if (next && typeof next === 'string' && next.trim() === ':') {
if (typeof current === 'string') {
result.push(`${current}:`)
} else if (React.isValidElement(current)) {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const element = current as React.ReactElement<any>
const currentChildren = element.props.children
result.push(
React.cloneElement(element, {
children:
typeof currentChildren === 'string'
? `${currentChildren}:`
: currentChildren,
}),
)
} else {
result.push(current)
result.push(next)
}
i += 2
} else {
result.push(current)
i += 1
}
}

return result
}

/**
* Displays a chat message with clickable text elements.
*
Expand Down Expand Up @@ -137,139 +89,10 @@ export function ChatMessage({
<MemoizedReactMarkdown
className="min-w-full prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0"
remarkPlugins={[remarkGfm, rehypeMathJax, remarkRehype]}
components={{
// Process paragraph nodes.
// @ts-ignore
p({ children }) {
return (
<p className="text-left whitespace-pre-line">
{preprocessChildren(children)}
</p>
)
},
// Process heading nodes with clickable functionality.
// @ts-ignore
h1({ children }) {
const text = getTextFromChildren(children)
return (
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<h1
className="mb-2 text-2xl font-bold cursor-pointer clickable-heading"
onClick={() => handleClickableClick(text)}
>
{preprocessChildren(children)}
</h1>
)
},
// @ts-ignore
h2({ children }) {
const text = getTextFromChildren(children)
return (
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<h2
className="mb-2 text-xl font-bold cursor-pointer clickable-heading"
onClick={() => handleClickableClick(text)}
>
{preprocessChildren(children)}
</h2>
)
},
// @ts-ignore
h3({ children }) {
const text = getTextFromChildren(children)
return (
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<h3
className="mb-2 text-lg font-bold cursor-pointer clickable-heading"
onClick={() => handleClickableClick(text)}
>
{preprocessChildren(children)}
</h3>
)
},
// Process strong/emphasis nodes.
// @ts-ignore
strong({ children }) {
return <strong>{preprocessChildren(children)}</strong>
},
// List handling.
// @ts-ignore
ul({ children }) {
return <ul className="ml-2 space-y-2 list-disc">{children}</ul>
},
// @ts-ignore
ol({ children }) {
return <ol className="ml-2 space-y-2 list-decimal">{children}</ol>
},
// @ts-ignore
li({ children }) {
const processedChildren = preprocessChildren(children)
const text = getTextFromChildren(processedChildren)
const hasNestedList = React.Children.toArray(
processedChildren,
).some(
(child) =>
React.isValidElement(child) &&
(child.type === 'ul' || child.type === 'ol'),
)

return (
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<li
className={cn(
'ml-4',
hasNestedList && 'mt-2',
'clickable-list-heading',
)}
onClick={() => handleClickableClick(text)}
>
{processedChildren}
</li>
)
},
// Process link nodes.
// @ts-ignore
a({ href, children, ...props }) {
return (
<a
className="text-blue-500 underline"
target="_blank"
rel="noopener noreferrer"
href={href}
{...props}
>
{children}
</a>
)
},
// Process code blocks.
// @ts-ignore
code({ inline, className, children, ...props }) {
const childrenText = String(children)
if (childrenText?.startsWith('▍')) {
return (
<span className="mt-1 cursor-default animate-pulse">▍</span>
)
}

const match = /language-(\w+)/.exec(className || '')
if (inline) {
return (
<code className={className} {...props}>
{children}
</code>
)
}
return (
<CodeBlock
key={Math.random()}
language={match?.[1] || ''}
value={String(children).replace(/\n$/, '')}
{...props}
/>
)
},
}}
components={memoizedMarkdownComponents({
handleClickableClick,
shouldPreProcessChildren: true,
})}
>
{cleanMessage.content}
</MemoizedReactMarkdown>
Expand Down
Loading