Open processed source versions through the existing preview

Why: Source context must remain attached to the bytes being read.

Evidence: SELF frontend tests and production build pass.

Impact: Carries an optional expected hash and preserves one preview location.

Refs: fast-slow-u2-u3 Task 5 source-version acceptance
This commit is contained in:
2026-10-13 02:51:07 +00:00
parent b00fa4433b
commit 175210f4bb
4 changed files with 8 additions and 7 deletions
+5 -4
View File
@@ -24,6 +24,7 @@ export function PreviewPane() {
})
: undefined
const art = active ? artifacts.find(a => a.id === active.id) : undefined
const contentUrl=art?api.artifactContentUrl(art.id)+(active?.sourceContext?.expectedHash?`?expectedHash=${encodeURIComponent(active.sourceContext.expectedHash)}`:''):''
const textKind = !!art && isTextKind(art)
const [content, setContent] = useState<string | null>(null)
const [editing, setEditing] = useState(false)
@@ -35,12 +36,12 @@ export function PreviewPane() {
setContent(null)
if (!art || !isTextKind(art)) return
let cancelled = false
fetch(api.artifactContentUrl(art.id))
.then(r => r.text())
fetch(contentUrl)
.then(r => {if(!r.ok)throw new Error('source unavailable');return r.text()})
.then(text => { if (!cancelled) setContent(text) })
.catch(() => { if (!cancelled) setContent('(读取失败)') })
return () => { cancelled = true }
}, [art?.id])
}, [art?.id,contentUrl])
if (!active) return null
if (active.id.startsWith('surface:')) {
@@ -120,7 +121,7 @@ export function PreviewPane() {
id: art.id,
name: art.name,
mime: art.mime,
contentUrl: api.artifactContentUrl(art.id),
contentUrl,
downloadUrl: api.artifactDownloadUrl(art.id),
}} />
) : <div className="empty-note" style={{ padding: 14 }}>这个文件已不在当前项目里</div>}
+1 -1
View File
@@ -19,7 +19,7 @@ export const docBridge: DocBridge = {
try{const out=await api.taskSource(cardId,source.id)
if(context!==store.getState().currentConvId||project!==store.getState().currentProjectId)return
if(!out.artifact){store.toast('来源当前不可用,正在核实工作记录');if(context)void store.refreshCards(context);return}
store.requestTaskSourcePreview(out.artifact,{notice:out.versionState==='changed'?'当前文件已更新;下方引用来自任务处理时的版本,不能作为当前文件的结论':'当前文件与处理时来源一致;下方是本项发现引用的片段',quote:out.source.quote??''})
store.requestTaskSourcePreview(out.artifact,{notice:out.versionState==='changed'?'当前文件已更新;下方引用来自任务处理时的版本,不能作为当前文件的结论':'任务处理时引用的来源版本;下方是本项发现引用的片段',quote:out.source.quote??'',expectedHash:out.versionState==='current'?out.source.contentHash:undefined})
if(out.versionState==='changed'&&context)void store.refreshCards(context)
}catch{if(context===store.getState().currentConvId&&project===store.getState().currentProjectId){store.toast('来源当前无法读取,正在核实工作记录');if(context)void store.refreshCards(context)}}
},
+1 -1
View File
@@ -3,7 +3,7 @@
export interface PreviewTab {
id: string
name: string
sourceContext?: {notice:string;quote:string}
sourceContext?: {notice:string;quote:string;expectedHash?:string}
}
export function openPreviewTab(tabs: PreviewTab[], tab: PreviewTab): { tabs: PreviewTab[]; activeId: string } {
+1 -1
View File
@@ -1255,7 +1255,7 @@ export const store = {
* 工件预览唯一入口:在第三栏「文件」入口打开或激活标签(多文件多标签)。
* 打开第一张自动切到预览子视图;关掉最后一张回到文件列表,并收回展开态。
*/
requestTaskSourcePreview(a:Artifact,sourceContext:{notice:string;quote:string}){
requestTaskSourcePreview(a:Artifact,sourceContext:{notice:string;quote:string;expectedHash?:string}){
store.requestPreview(a)
const conv=previewTabsKey(state.currentConvId)
set({previewTabs:{...state.previewTabs,[conv]:(state.previewTabs[conv]??[]).map(tab=>tab.id===a.id?{...tab,sourceContext}:tab)}})