diff --git a/ee/tabby-ui/components/message-markdown/doc-detail-view.tsx b/ee/tabby-ui/components/message-markdown/doc-detail-view.tsx
new file mode 100644
index 000000000000..999a41b2d1f8
--- /dev/null
+++ b/ee/tabby-ui/components/message-markdown/doc-detail-view.tsx
@@ -0,0 +1,153 @@
+import DOMPurify from 'dompurify'
+import he from 'he'
+import { marked } from 'marked'
+
+import { Maybe } from '@/lib/gql/generates/graphql'
+import type { AttachmentDocItem } from '@/lib/types'
+import { getContent } from '@/lib/utils'
+
+import { SiteFavicon } from '../site-favicon'
+import { Badge } from '../ui/badge'
+import {
+ IconCheckCircled,
+ IconCircleDot,
+ IconGitMerge,
+ IconGitPullRequest
+} from '../ui/icons'
+import { UserAvatar } from '../user-avatar'
+
+export function DocDetailView({
+ relevantDocument
+}: {
+ relevantDocument: AttachmentDocItem
+}) {
+ const sourceUrl = relevantDocument ? new URL(relevantDocument.link) : null
+ const isIssue = relevantDocument?.__typename === 'MessageAttachmentIssueDoc'
+ const isPR = relevantDocument?.__typename === 'MessageAttachmentPullDoc'
+ const author =
+ relevantDocument.__typename === 'MessageAttachmentWebDoc'
+ ? undefined
+ : relevantDocument.author
+
+ return (
+
+
+
+
+
{sourceUrl!.hostname}
+
+
window.open(relevantDocument.link)}
+ >
+ {relevantDocument.title}
+
+
+ {isIssue && (
+
+ )}
+ {isPR && (
+
+ )}
+
+
+ {normalizedText(getContent(relevantDocument))}
+
+
+
+ )
+}
+
+function PullDocInfoView({
+ merged,
+ user
+}: {
+ merged: boolean
+ user: Maybe<{ id: string; email: string; name: string }> | undefined
+}) {
+ return (
+
+
+
+ {!!user && (
+ <>
+
+
+ {user.name || user.email}
+
+ >
+ )}
+
+
+ )
+}
+
+function IssueDocInfoView({
+ closed,
+ user
+}: {
+ closed: boolean
+ user: Maybe<{ id: string; email: string; name: string }> | undefined
+}) {
+ return (
+
+
+
+ {!!user && (
+ <>
+
+
+ {user.name || user.email}
+
+ >
+ )}
+
+
+ )
+}
+
+function IssueStateBadge({ closed }: { closed: boolean }) {
+ return (
+
+ {closed ? (
+
+ ) : (
+
+ )}
+ {closed ? 'Closed' : 'Open'}
+
+ )
+}
+
+function PRStateBadge({ merged }: { merged: boolean }) {
+ return (
+
+ {merged ? (
+
+ ) : (
+
+ )}
+ {merged ? 'Merged' : 'Open'}
+
+ )
+}
+
+const normalizedText = (input: string) => {
+ const sanitizedHtml = DOMPurify.sanitize(input, {
+ ALLOWED_TAGS: [],
+ ALLOWED_ATTR: []
+ })
+ const parsed = marked.parse(sanitizedHtml) as string
+ const decoded = he.decode(parsed)
+ const plainText = decoded.replace(/<\/?[^>]+(>|$)/g, '')
+ return plainText
+}
diff --git a/ee/tabby-ui/components/message-markdown/index.tsx b/ee/tabby-ui/components/message-markdown/index.tsx
index 248241d2cde3..c93fc3d8647d 100644
--- a/ee/tabby-ui/components/message-markdown/index.tsx
+++ b/ee/tabby-ui/components/message-markdown/index.tsx
@@ -1,10 +1,5 @@
import { ReactNode, useContext, useMemo, useState } from 'react'
-import Image from 'next/image'
-import defaultFavicon from '@/assets/default-favicon.png'
-import DOMPurify from 'dompurify'
-import he from 'he'
import { compact, isNil } from 'lodash-es'
-import { marked } from 'marked'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
@@ -14,7 +9,7 @@ import {
MessageAttachmentClientCode
} from '@/lib/gql/generates/graphql'
import { AttachmentCodeItem, AttachmentDocItem } from '@/lib/types'
-import { cn, getContent } from '@/lib/utils'
+import { cn } from '@/lib/utils'
import {
HoverCard,
HoverCardContent,
@@ -37,15 +32,9 @@ import {
} from '@/lib/constants/regex'
import { Mention } from '../mention-tag'
-import { Badge } from '../ui/badge'
-import {
- IconCheckCircled,
- IconCircleDot,
- IconGitMerge,
- IconGitPullRequest
-} from '../ui/icons'
import { Skeleton } from '../ui/skeleton'
import { CodeElement } from './code'
+import { DocDetailView } from './doc-detail-view'
import { MessageMarkdownContext } from './markdown-context'
type RelevantDocItem = {
@@ -61,17 +50,6 @@ type RelevantCodeItem = {
type MessageAttachments = Array
-const normalizedText = (input: string) => {
- const sanitizedHtml = DOMPurify.sanitize(input, {
- ALLOWED_TAGS: [],
- ALLOWED_ATTR: []
- })
- const parsed = marked.parse(sanitizedHtml) as string
- const decoded = he.decode(parsed)
- const plainText = decoded.replace(/<\/?[^>]+(>|$)/g, '')
- return plainText
-}
-
export interface MessageMarkdownProps {
message: string
headline?: boolean
@@ -381,12 +359,8 @@ function RelevantDocumentBadge({
relevantDocument: AttachmentDocItem
citationIndex: number
}) {
- const sourceUrl = relevantDocument ? new URL(relevantDocument.link) : null
- const isIssue = relevantDocument?.__typename === 'MessageAttachmentIssueDoc'
- const isPR = relevantDocument?.__typename === 'MessageAttachmentPullDoc'
-
return (
-
+
-
-
-
-
-
{sourceUrl!.hostname}
-
-
window.open(relevantDocument.link)}
- >
- {relevantDocument.title}
-
-
- {isIssue &&
}
- {isPR &&
}
-
-
- {normalizedText(getContent(relevantDocument))}
-
-
+
+
)
@@ -453,78 +406,3 @@ function RelevantCodeBadge({
)
}
-
-export function SiteFavicon({
- hostname,
- className
-}: {
- hostname: string
- className?: string
-}) {
- const [isLoaded, setIsLoaded] = useState(false)
-
- const handleImageLoad = () => {
- setIsLoaded(true)
- }
-
- return (
-
-
-
-
- )
-}
-
-function IssueStateBadge({ closed }: { closed: boolean }) {
- return (
-
- {closed ? (
-
- ) : (
-
- )}
- {closed ? 'Closed' : 'Open'}
-
- )
-}
-
-function PRStateBadge({ merged }: { merged: boolean }) {
- return (
-
- {merged ? (
-
- ) : (
-
- )}
- {merged ? 'Merged' : 'Open'}
-
- )
-}
diff --git a/ee/tabby-ui/components/site-favicon.tsx b/ee/tabby-ui/components/site-favicon.tsx
new file mode 100644
index 000000000000..33b65bb37d81
--- /dev/null
+++ b/ee/tabby-ui/components/site-favicon.tsx
@@ -0,0 +1,48 @@
+import { useState } from 'react'
+import Image from 'next/image'
+import defaultFavicon from '@/assets/default-favicon.png'
+
+import { cn } from '@/lib/utils'
+
+export function SiteFavicon({
+ hostname,
+ className
+}: {
+ hostname: string
+ className?: string
+}) {
+ const [isLoaded, setIsLoaded] = useState(false)
+
+ const handleImageLoad = () => {
+ setIsLoaded(true)
+ }
+
+ return (
+
+
+
+
+ )
+}
diff --git a/ee/tabby-ui/components/ui/hover-card.tsx b/ee/tabby-ui/components/ui/hover-card.tsx
index 0ec79708ab32..0691ebcaafd0 100644
--- a/ee/tabby-ui/components/ui/hover-card.tsx
+++ b/ee/tabby-ui/components/ui/hover-card.tsx
@@ -8,7 +8,7 @@ import { cn } from '@/lib/utils'
const HoverCard = HoverCardPrimitive.Root
const HoverCardTrigger = HoverCardPrimitive.Trigger
-
+const HoverCardPortal = HoverCardPrimitive.Portal
const HoverCardContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
@@ -26,4 +26,4 @@ const HoverCardContent = React.forwardRef<
))
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
-export { HoverCard, HoverCardTrigger, HoverCardContent }
+export { HoverCard, HoverCardTrigger, HoverCardContent, HoverCardPortal }
diff --git a/ee/tabby-ui/lib/hooks/use-thread-run.ts b/ee/tabby-ui/lib/hooks/use-thread-run.ts
index 0a78fe90aa91..ae8a264ac610 100644
--- a/ee/tabby-ui/lib/hooks/use-thread-run.ts
+++ b/ee/tabby-ui/lib/hooks/use-thread-run.ts
@@ -65,12 +65,22 @@ const CreateThreadAndRunSubscription = graphql(/* GraphQL */ `
... on MessageAttachmentIssueDoc {
title
link
+ author {
+ id
+ email
+ name
+ }
body
closed
}
... on MessageAttachmentPullDoc {
title
link
+ author {
+ id
+ email
+ name
+ }
body
merged
}
@@ -132,12 +142,22 @@ const CreateThreadRunSubscription = graphql(/* GraphQL */ `
... on MessageAttachmentIssueDoc {
title
link
+ author {
+ id
+ email
+ name
+ }
body
closed
}
... on MessageAttachmentPullDoc {
title
link
+ author {
+ id
+ email
+ name
+ }
body
merged
}
diff --git a/ee/tabby-ui/lib/tabby/query.ts b/ee/tabby-ui/lib/tabby/query.ts
index 10375f3025da..c1088a2ddad1 100644
--- a/ee/tabby-ui/lib/tabby/query.ts
+++ b/ee/tabby-ui/lib/tabby/query.ts
@@ -422,12 +422,22 @@ export const listThreadMessages = graphql(/* GraphQL */ `
... on MessageAttachmentIssueDoc {
title
link
+ author {
+ id
+ email
+ name
+ }
body
closed
}
... on MessageAttachmentPullDoc {
title
link
+ author {
+ id
+ email
+ name
+ }
body
merged
}