Files
Agent-System-Frontend/src/components/PreviewPane.tsx
T
weichenandCursor 0ed8ad32f1 Show assistant work as a timeline and preview files in tabs
Why: Process output folded on every text delta, successful tool
results disappeared after refresh, and generated files stayed plain
text instead of a card that opens a closable preview.

Evidence: npx tsc -b --noEmit (clean); npx vitest run (51 passed).

Impact: Live work stays expanded until the run ends, then folds to
one worked-time line. File cards open a per-conversation preview pane.
docx renders page by page, with mammoth if that fails.

Refs: feature/chat-timeline-preview
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 06:15:44 +00:00

51 lines
2.0 KiB
TypeScript

// 对话右侧预览区:多标签、可关、全屏与下载。宽度由 App 的分隔条控制。
import { FilePreview, TabStrip, Icon } from '@ui'
import { api } from '../api'
import { store, useStore } from '../store'
export function PreviewPane() {
const convId = useStore(s => s.currentConvId)
const tabs = useStore(s => (convId ? s.previewTabs[convId] : undefined) ?? [])
const activeId = useStore(s => (convId ? s.previewActive[convId] : null) ?? null)
const artifacts = useStore(s => s.artifacts)
const active = tabs.find(t => t.id === activeId) ?? tabs[0]
if (!active) return null
const art = artifacts.find(a => a.id === active.id)
return (
<div className="preview-pane">
<TabStrip
tabs={tabs.map(t => ({ id: t.id, label: t.name }))}
activeId={active.id}
onActivate={id => store.activatePreview(id)}
onClose={id => store.closePreview(id)}
/>
<div className="preview-pane-bar">
<span className="preview-pane-name" title={active.name}>{active.name}</span>
<span className="preview-pane-spacer" />
{art && (
<>
<button type="button" className="icon-btn" title="全屏预览" onClick={() => store.requestPreview(art, 'full')}>
<Icon name="eye" size={14} />
</button>
<a className="icon-btn" title="下载" href={api.artifactDownloadUrl(art.id)} download={art.name}>
<Icon name="download" size={14} />
</a>
</>
)}
</div>
<div className="preview-pane-body">
{art ? (
<FilePreview source={{
kind: 'artifact',
id: art.id,
name: art.name,
mime: art.mime,
contentUrl: api.artifactContentUrl(art.id),
downloadUrl: api.artifactDownloadUrl(art.id),
}} />
) : <div className="empty-note" style={{ padding: 14 }}>这个文件已不在当前项目里</div>}
</div>
</div>
)
}