diff --git a/src/components/ArtifactsPanel.tsx b/src/components/ArtifactsPanel.tsx new file mode 100644 index 0000000..c830b17 --- /dev/null +++ b/src/components/ArtifactsPanel.tsx @@ -0,0 +1,67 @@ +// 工件面板(工作区文件管理):搜索 + kind 过滤 + 上传;行内深预览/右栏预览/下载。 +// ⌘1 弹层与右栏「文件」页签共用这一份。删除仍是保留能力,系统默认不提供入口。 +import { useState } from 'react' +import { store, useStore } from '../store' +import { api } from '../api' +import { Icon } from '@ui' +import { artifactIcon, humanSize } from '../messageparts' + +export function ArtifactsPanel() { + const artifacts = useStore(s => s.artifacts) + const [q, setQ] = useState('') + const [kind, setKind] = useState<'all' | 'document' | 'data' | 'image'>('all') + + const kinds = ['all', 'document', 'data', 'image'] as const + const visible = artifacts.filter(a => + (kind === 'all' || a.kind === kind) && a.name.toLowerCase().includes(q.toLowerCase())) + + return ( +
+
+ setQ(e.target.value)} /> + {kinds.map(k => ( + + ))} + +
+ {artifacts.length === 0 + ?
暂无文件——上传附件或让模型产出文档(mock:#doc);这里可浏览、预览、下载
+ : visible.length === 0 + ?
没有匹配「{q}」的文件
+ : visible.map(a => ( + // 预览落位在右栏——弹层必须先收起,否则预览换到被盖住的右栏。 +
{ store.closeWorkspace(); store.requestPreview(a) }} title={`预览 ${a.name}(右栏,边聊边看)`}> + + + {a.name} + {a.kind} · {humanSize(a.size) || '—'} · {shortTime(a.updatedAt)} + + + + e.stopPropagation()}> + +
+ ))} +
+ ) +} + +function shortTime(iso: string): string { + try { + const d = new Date(iso) + const now = new Date() + return d.toDateString() === now.toDateString() + ? d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + : d.toLocaleDateString([], { month: '2-digit', day: '2-digit' }) + } catch { return '' } +} diff --git a/src/components/Conversation.tsx b/src/components/Conversation.tsx index 77e5075..85659f6 100644 --- a/src/components/Conversation.tsx +++ b/src/components/Conversation.tsx @@ -318,6 +318,40 @@ function CommandMenu({ text, onPick }: { text: string; onPick: (cmd: string) => ) } +function OutboxPanel() { + const outbox = useStore(s => s.outbox) + const [editing, setEditing] = useState(null) + const [draft, setDraft] = useState('') + if (outbox.length === 0) return null + return ( + 排队 {outbox.length}} + triggerClassName="outbox-trigger"> + + + ) +} + export function Conversation() { const currentConvId = useStore(s => s.currentConvId) const conversation = useStore(s => s.tasks.find(c => c.id === s.currentConvId)) @@ -349,8 +383,14 @@ export function Conversation() { const send = async () => { if (!text.trim() && attachmentIds.length === 0) return const t = text + const ids = attachmentIds + const names = attachmentNames setText(''); setAttachmentIds([]); setAttachmentNames([]) - await store.sendMessage(t, attachmentIds) + if (active) { + store.enqueueOutbox(t, ids, names) + return + } + await store.sendMessage(t, ids) } if (!currentConvId) { @@ -431,6 +471,7 @@ export function Conversation() {
+ {attachmentNames.length > 0 && (
{attachmentNames.map((n, i) => {n} @@ -450,15 +491,14 @@ export function Conversation() { }} />