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>
51 lines
2.0 KiB
TypeScript
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>
|
|
)
|
|
}
|