Files
infio-copilot-dev/src/components/chat-view/chat-input/MentionableBadge.tsx
2025-04-22 22:10:22 +08:00

320 lines
6.3 KiB
TypeScript

import { X } from 'lucide-react'
import { PropsWithChildren } from 'react'
import {
Mentionable,
MentionableBlock,
MentionableCurrentFile,
MentionableFile,
MentionableFolder,
MentionableImage,
MentionableUrl,
MentionableVault,
} from '../../../types/mentionable'
import { getMentionableIcon } from './utils/get-metionable-icon'
function BadgeBase({
children,
onDelete,
onClick,
isFocused,
}: PropsWithChildren<{
onDelete: () => void
onClick: () => void
isFocused: boolean
}>) {
return (
<div
className={`infio-chat-user-input-file-badge ${isFocused ? 'infio-chat-user-input-file-badge-focused' : ''}`}
onClick={onClick}
>
{children}
<div
className="infio-chat-user-input-file-badge-delete"
onClick={(evt) => {
evt.stopPropagation()
onDelete()
}}
>
<X size={16} />
</div>
</div>
)
}
function FileBadge({
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableFile
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
<div className="infio-chat-user-input-file-badge-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-icon"
/>
)}
<span>{mentionable.file.name}</span>
</div>
</BadgeBase>
)
}
function FolderBadge({
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableFolder
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
<div className="infio-chat-user-input-file-badge-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-icon"
/>
)}
<span>{mentionable.folder.name}</span>
</div>
</BadgeBase>
)
}
function VaultBadge({
// eslint-disable-next-line @typescript-eslint/no-unused-vars
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableVault
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
{/* TODO: Update style */}
<div className="infio-chat-user-input-file-badge-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-icon"
/>
)}
<span>Vault</span>
</div>
</BadgeBase>
)
}
function CurrentFileBadge({
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableCurrentFile
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return mentionable.file ? (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
<div className="infio-chat-user-input-file-badge-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-icon"
/>
)}
<span>{mentionable.file.name}</span>
</div>
<div className="infio-chat-user-input-file-badge-name-block-suffix">
{' (Current file)'}
</div>
</BadgeBase>
) : null
}
function BlockBadge({
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableBlock
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
<div className="infio-chat-user-input-file-badge-name-block-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-block-name-icon"
/>
)}
<span>{mentionable.file.name}</span>
</div>
<div className="infio-chat-user-input-file-badge-name-block-suffix">
{` (${mentionable.startLine}:${mentionable.endLine})`}
</div>
</BadgeBase>
)
}
function UrlBadge({
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableUrl
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
<div className="infio-chat-user-input-file-badge-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-icon"
/>
)}
<span>{mentionable.url}</span>
</div>
</BadgeBase>
)
}
function ImageBadge({
mentionable,
onDelete,
onClick,
isFocused,
}: {
mentionable: MentionableImage
onDelete: () => void
onClick: () => void
isFocused: boolean
}) {
const Icon = getMentionableIcon(mentionable)
return (
<BadgeBase onDelete={onDelete} onClick={onClick} isFocused={isFocused}>
<div className="infio-chat-user-input-file-badge-name">
{Icon && (
<Icon
size={10}
className="infio-chat-user-input-file-badge-name-icon"
/>
)}
<span>{mentionable.name}</span>
</div>
</BadgeBase>
)
}
export default function MentionableBadge({
mentionable,
onDelete,
onClick,
isFocused = false,
}: {
mentionable: Mentionable
onDelete: () => void
onClick: () => void
isFocused?: boolean
}) {
switch (mentionable.type) {
case 'file':
return (
<FileBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
case 'folder':
return (
<FolderBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
case 'vault':
return (
<VaultBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
case 'current-file':
return (
<CurrentFileBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
case 'block':
return (
<BlockBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
case 'url':
return (
<UrlBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
case 'image':
return (
<ImageBadge
mentionable={mentionable}
onDelete={onDelete}
onClick={onClick}
isFocused={isFocused}
/>
)
}
}