mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(mothership): restore attachment previews on draft and add video support (#4435)
* feat(mothership): restore attachment previews on draft and add video support * fix(mothership): icon fallback behind video preview
This commit is contained in:
@@ -30,13 +30,36 @@ export function ChatMessageAttachments(props: {
|
||||
)}
|
||||
>
|
||||
{attachments.map((att) => {
|
||||
const isImage = att.media_type.startsWith('image/')
|
||||
return isImage && att.previewUrl ? (
|
||||
if (!att.previewUrl) {
|
||||
return (
|
||||
<FileAttachmentPill key={att.id} mediaType={att.media_type} filename={att.filename} />
|
||||
)
|
||||
}
|
||||
const isVideo = att.media_type.startsWith('video/')
|
||||
if (isVideo) {
|
||||
const Icon = getDocumentIcon(att.media_type, att.filename)
|
||||
return (
|
||||
<div
|
||||
key={att.id}
|
||||
className='relative h-[56px] w-[56px] overflow-hidden rounded-[8px] bg-[var(--surface-5)]'
|
||||
>
|
||||
<div className='absolute inset-0 flex items-center justify-center text-[var(--text-icon)]'>
|
||||
<Icon className='h-[18px] w-[18px]' />
|
||||
</div>
|
||||
<video
|
||||
src={att.previewUrl}
|
||||
muted
|
||||
playsInline
|
||||
preload='metadata'
|
||||
className='relative h-full w-full object-cover'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div key={att.id} className='h-[56px] w-[56px] overflow-hidden rounded-[8px]'>
|
||||
<img src={att.previewUrl} alt={att.filename} className='h-full w-full object-cover' />
|
||||
</div>
|
||||
) : (
|
||||
<FileAttachmentPill key={att.id} mediaType={att.media_type} filename={att.filename} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
+19
-2
@@ -22,7 +22,8 @@ export const AttachedFilesList = React.memo(function AttachedFilesList({
|
||||
return (
|
||||
<div className='mb-1.5 flex flex-wrap gap-1.5'>
|
||||
{attachedFiles.map((file) => {
|
||||
const isImage = file.type.startsWith('image/')
|
||||
const isVideo = file.type.startsWith('video/')
|
||||
const hasPreview = Boolean(file.previewUrl)
|
||||
return (
|
||||
<Tooltip.Root key={file.id}>
|
||||
<Tooltip.Trigger asChild>
|
||||
@@ -30,7 +31,23 @@ export const AttachedFilesList = React.memo(function AttachedFilesList({
|
||||
className='group relative h-[56px] w-[56px] flex-shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-5)] hover:bg-[var(--surface-4)]'
|
||||
onClick={() => onFileClick(file)}
|
||||
>
|
||||
{isImage && file.previewUrl ? (
|
||||
{hasPreview && isVideo ? (
|
||||
<>
|
||||
<div className='absolute inset-0 flex items-center justify-center text-[var(--text-icon)]'>
|
||||
{(() => {
|
||||
const Icon = getDocumentIcon(file.type, file.name)
|
||||
return <Icon className='h-[18px] w-[18px]' />
|
||||
})()}
|
||||
</div>
|
||||
<video
|
||||
src={file.previewUrl}
|
||||
muted
|
||||
playsInline
|
||||
preload='metadata'
|
||||
className='relative h-full w-full object-cover'
|
||||
/>
|
||||
</>
|
||||
) : hasPreview ? (
|
||||
<img
|
||||
src={file.previewUrl}
|
||||
alt={file.name}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Paperclip } from 'lucide-react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Button, Tooltip } from '@/components/emcn'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getMothershipAttachmentPreviewUrl } from '@/lib/copilot/chat/attachment-preview'
|
||||
import { SIM_RESOURCE_DRAG_TYPE, SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { CHAT_ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
|
||||
@@ -211,6 +212,7 @@ export const UserInput = forwardRef<UserInputHandle, UserInputProps>(function Us
|
||||
path: a.path ?? '',
|
||||
key: a.key,
|
||||
uploading: false,
|
||||
previewUrl: getMothershipAttachmentPreviewUrl(a),
|
||||
}))
|
||||
}
|
||||
if (typeof draft.text === 'string' && draft.text.length > 0) {
|
||||
@@ -385,6 +387,7 @@ export const UserInput = forwardRef<UserInputHandle, UserInputProps>(function Us
|
||||
path: a.path ?? '',
|
||||
key: a.key,
|
||||
uploading: false,
|
||||
previewUrl: getMothershipAttachmentPreviewUrl(a),
|
||||
}))
|
||||
files.restoreAttachedFiles(restored)
|
||||
contextManagement.setSelectedContexts(msg.contexts ?? [])
|
||||
|
||||
@@ -5,6 +5,7 @@ import { sleep } from '@sim/utils/helpers'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { getMothershipAttachmentPreviewUrl } from '@/lib/copilot/chat/attachment-preview'
|
||||
import { toDisplayMessage } from '@/lib/copilot/chat/display-message'
|
||||
import { getLiveAssistantMessageId } from '@/lib/copilot/chat/effective-transcript'
|
||||
import type {
|
||||
@@ -3303,9 +3304,7 @@ export function useChat(
|
||||
filename: f.filename,
|
||||
media_type: f.media_type,
|
||||
size: f.size,
|
||||
previewUrl: f.media_type.startsWith('image/')
|
||||
? `/api/files/serve/${encodeURIComponent(f.key)}?context=mothership`
|
||||
: undefined,
|
||||
previewUrl: getMothershipAttachmentPreviewUrl(f),
|
||||
}))
|
||||
|
||||
const optimisticUserMessage: ChatMessage = {
|
||||
|
||||
+4
-1
@@ -126,7 +126,10 @@ export function useFileAttachments(props: UseFileAttachmentsProps) {
|
||||
type: resolveFileType(file),
|
||||
path: '',
|
||||
uploading: true,
|
||||
previewUrl: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,
|
||||
previewUrl:
|
||||
file.type.startsWith('image/') || file.type.startsWith('video/')
|
||||
? URL.createObjectURL(file)
|
||||
: undefined,
|
||||
}))
|
||||
|
||||
setAttachedFiles((prev) => [...prev, ...placeholders])
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export function getMothershipAttachmentPreviewUrl(file: {
|
||||
key: string
|
||||
media_type: string
|
||||
}): string | undefined {
|
||||
if (!file.media_type.startsWith('image/') && !file.media_type.startsWith('video/')) {
|
||||
return undefined
|
||||
}
|
||||
return `/api/files/serve/${encodeURIComponent(file.key)}?context=mothership`
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
type ToolCallInfo,
|
||||
ToolCallStatus,
|
||||
} from '@/app/workspace/[workspaceId]/home/types'
|
||||
import { getMothershipAttachmentPreviewUrl } from './attachment-preview'
|
||||
import type { PersistedContentBlock, PersistedMessage } from './persisted-message'
|
||||
import { withBlockTiming } from './persisted-message'
|
||||
|
||||
@@ -91,9 +92,7 @@ function toDisplayAttachment(f: PersistedMessage['fileAttachments']): ChatMessag
|
||||
filename: a.filename,
|
||||
media_type: a.media_type,
|
||||
size: a.size,
|
||||
previewUrl: a.media_type.startsWith('image/')
|
||||
? `/api/files/serve/${encodeURIComponent(a.key)}?context=mothership`
|
||||
: undefined,
|
||||
previewUrl: getMothershipAttachmentPreviewUrl(a),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user