1010 */
1111import { useEffect , useState } from "react" ;
1212import { invoke } from "@tauri-apps/api/core" ;
13+ import { convertFileSrc } from "@tauri-apps/api/core" ;
1314import ArtifactView from "../components/ArtifactView" ;
1415import ImageGallery from "../components/ImageGallery" ;
1516import NotePreviewView from "../components/NotePreviewView" ;
@@ -49,6 +50,8 @@ export default function SessionDetailPanel({ detail, fusing, degradedBanner, onT
4950 const [ quality , setQuality ] = useState < QualityReport | null > ( null ) ;
5051 // M6(REQ-077):大纲(产物视图侧边导航)
5152 const [ outline , setOutline ] = useState < OutlineEntry [ ] > ( [ ] ) ;
53+ // v0.7.3(REQ-160):屏卡配图 baseUrl(图集同款:convertFileSrc 拼本地路径)
54+ const [ baseUrl , setBaseUrl ] = useState ( "" ) ;
5255 const sessionId = detail . session . id ;
5356
5457 // 质量报告 + 大纲随详情加载(失败不阻断详情展示)
@@ -62,6 +65,9 @@ export default function SessionDetailPanel({ detail, fusing, degradedBanner, onT
6265 void invoke < OutlineEntry [ ] > ( "session_outline" , { id : sessionId } )
6366 . then ( setOutline )
6467 . catch ( ( ) => undefined ) ;
68+ void invoke < string > ( "session_images_base_url" , { sessionId } )
69+ . then ( setBaseUrl )
70+ . catch ( ( ) => setBaseUrl ( "" ) ) ;
6571 } , [ sessionId ] ) ;
6672
6773 return (
@@ -230,18 +236,91 @@ export default function SessionDetailPanel({ detail, fusing, degradedBanner, onT
230236 ) ) }
231237 </ div >
232238
233- { /* 画面要点 */ }
234- < h3 style = { { fontSize : 13 , margin : "16px 0 6px" } } > 画面要点(OCR)</ h3 >
235- < div style = { { display : "flex" , flexDirection : "column" , gap : 3 } } >
236- { detail . ocr_blocks . length === 0 && < p style = { { fontSize : 12 , color : "#9ca3af" } } > 本会话无画面识别内容</ p > }
237- { detail . ocr_blocks . map ( ( b ) => (
238- < div key = { b . id } id = { `ocr-${ sessionId } -${ b . timestamp_ms } ` } style = { { fontSize : 12 , color : "#4b5563" } } >
239- < span style = { { color : "#9ca3af" , fontVariantNumeric : "tabular-nums" } } > [{ fmtMs ( b . timestamp_ms ) } ]</ span > { " " }
240- { b . text }
241- { b . region === "subtitle" && < span style = { { color : "#0d9488" , marginLeft : 4 } } > 字幕</ span > }
239+ { /* 画面要点(v0.7.3 屏卡流:区间+标题+正文+标签+配图+结构徽标;可展开块级明细复查) */ }
240+ < h3 style = { { fontSize : 13 , margin : "16px 0 6px" } } >
241+ 画面要点(OCR)· { detail . screens . length } 屏
242+ < span style = { { color : "#9ca3af" , fontWeight : 400 } } >
243+ { detail . screens . length === 0 ? "" : `(原始 ${ detail . ocr_blocks . length } 块)` }
244+ </ span >
245+ </ h3 >
246+ { detail . screens . length === 0 && (
247+ < p style = { { fontSize : 12 , color : "#9ca3af" } } > 本会话无画面识别内容</ p >
248+ ) }
249+ { detail . screens . map ( ( s , i ) => {
250+ // 块级明细(原料复查):屏时间区间内的原始块
251+ const raw = detail . ocr_blocks . filter (
252+ ( b ) => b . timestamp_ms >= s . first_seen_ms && b . timestamp_ms <= s . last_seen_ms ,
253+ ) ;
254+ return (
255+ < div
256+ key = { i }
257+ id = { `ocr-${ sessionId } -${ s . first_seen_ms } ` }
258+ style = { {
259+ border : "1px solid #e5e7eb" ,
260+ borderRadius : 8 ,
261+ padding : "8px 10px" ,
262+ marginBottom : 8 ,
263+ background : "#fafafa" ,
264+ } }
265+ >
266+ < div style = { { fontSize : 11 , color : "#0f766e" , fontWeight : 600 , marginBottom : 4 } } >
267+ 📄 屏 { s . screen_id ?? i + 1 } · { fmtMs ( s . first_seen_ms ) } – { fmtMs ( s . last_seen_ms ) }
268+ { s . structure . length > 0 &&
269+ s . structure . map ( ( st , j ) => (
270+ < span key = { j } style = { { marginLeft : 8 , color : "#7c3aed" } } >
271+ { st . kind === "table" ? "📊" : st . kind === "formula" ? "∑" : "⟨code⟩" } { st . kind }
272+ </ span >
273+ ) ) }
274+ </ div >
275+ { s . title && (
276+ < div style = { { fontSize : 14 , fontWeight : 600 , color : "#111827" , marginBottom : 2 } } >
277+ { s . title }
278+ </ div >
279+ ) }
280+ { s . body . map ( ( b , j ) => (
281+ < div key = { j } style = { { fontSize : 12.5 , color : "#374151" , lineHeight : 1.6 } } >
282+ { b }
283+ </ div >
284+ ) ) }
285+ { s . labels . length > 0 && (
286+ < div style = { { fontSize : 11.5 , color : "#6b7280" , marginTop : 3 } } >
287+ 标签:{ s . labels . join ( " · " ) }
288+ </ div >
289+ ) }
290+ { s . structure . length > 0 && (
291+ < div style = { { fontSize : 11.5 , color : "#7c3aed" , marginTop : 3 } } >
292+ { s . structure . map ( ( st , j ) => (
293+ < div key = { j } > [{ st . kind } ] { st . rendered ?? st . text . slice ( 0 , 60 ) } </ div >
294+ ) ) }
295+ </ div >
296+ ) }
297+ { s . image_ref && baseUrl && (
298+ < img
299+ src = { convertFileSrc ( `${ baseUrl } /${ s . image_ref } ` ) }
300+ alt = { `屏 ${ i + 1 } ` }
301+ loading = "lazy"
302+ style = { {
303+ maxWidth : 260 ,
304+ marginTop : 6 ,
305+ borderRadius : 6 ,
306+ border : "1px solid #e5e7eb" ,
307+ display : "block" ,
308+ } }
309+ />
310+ ) }
311+ { raw . length > 0 && (
312+ < details style = { { fontSize : 11 , color : "#9ca3af" , marginTop : 4 } } >
313+ < summary style = { { cursor : "pointer" } } > 块级明细({ raw . length } 块,可复查误合并)</ summary >
314+ { raw . map ( ( b ) => (
315+ < div key = { b . id } >
316+ [{ fmtMs ( b . timestamp_ms ) } ] { b . text }
317+ </ div >
318+ ) ) }
319+ </ details >
320+ ) }
242321 </ div >
243- ) ) }
244- </ div >
322+ ) ;
323+ } ) }
245324
246325 { /* 参考图集(v0.5.0 M6:REQ-051 三层图结构) */ }
247326 < h3 style = { { fontSize : 13 , margin : "16px 0 6px" } } > 参考图集</ h3 >
0 commit comments