window.openCaseDetail = function (p, initialIdx) { if (!window._currentRawCasesContext) return; const ctx = window._currentRawCasesContext; // Determine the list of platforms to aggregate. let platformsToAggregate = [p]; if (p !== 'filtered_cases' && p !== 'source_ex') { const crawlerPlatforms = Object.keys(ctx.rawCasesObj).filter(k => k !== 'source' && k !== 'case_detailed' && k !== 'case' && k !== 'images' && k !== 'filtered_cases' && k !== 'source_ex'); platformsToAggregate = [...crawlerPlatforms, 'source']; } let casesList = []; let globalInitialIdx = 0; const seenIds = new Set(); platformsToAggregate.forEach(plat => { if (!ctx.rawCasesObj[plat]) return; let platCases = []; if (Array.isArray(ctx.rawCasesObj[plat])) { platCases = ctx.rawCasesObj[plat]; } else if (ctx.rawCasesObj[plat].cases) { platCases = ctx.rawCasesObj[plat].cases; } else if (ctx.rawCasesObj[plat].sources) { platCases = ctx.rawCasesObj[plat].sources; } else if (ctx.rawCasesObj[plat].by_reason) { Object.entries(ctx.rawCasesObj[plat].by_reason).forEach(([reasonKey, reasonObj]) => { if (reasonObj.sources && Array.isArray(reasonObj.sources)) { reasonObj.sources.forEach(src => { if (!src.filter_reason) src.filter_reason = reasonKey; platCases.push(src); }); } }); } platCases.forEach((c, idx) => { const cId = c.case_id || (c._raw && c._raw.case_id) || (c.post && c.post.channel_content_id) || `temp_${plat}_${idx}`; const cUrl = c.source_url || c.url || (c.post && c.post.link) || ''; const mappedS = ctx.sourceMap[cId] || ctx.sourceMap[cUrl] || (c._raw && ctx.sourceMap[c._raw.case_id]); if (plat !== 'filtered_cases' && plat !== 'source' && plat !== 'source_ex' && !mappedS) return; if (seenIds.has(cId)) return; seenIds.add(cId); const augmentedC = { ...c, _actualPlatform: plat }; if (plat === p && idx === initialIdx) { globalInitialIdx = casesList.length; } casesList.push(augmentedC); }); }); window._currentModalCases = casesList; window._currentModalPlatform = p; window._currentModalContext = ctx; window._currentModalIdx = globalInitialIdx; // Build Sidebar let sidebarHtml = ''; // Build Main Content Skeleton const mainHtml = `
`; document.getElementById('case-detail-modal-body').innerHTML = sidebarHtml + mainHtml; // Render the selected case window.renderSingleCaseDetail(globalInitialIdx); document.getElementById('case-detail-modal').classList.remove('hidden'); // Scroll sidebar to active item setTimeout(() => { const activeItem = document.getElementById(`sidebar-item-${globalInitialIdx}`); if (activeItem) activeItem.scrollIntoView({ block: 'nearest' }); }, 10); }; window.renderSingleCaseDetail = function (idx) { window._currentModalIdx = idx; const ctx = window._currentModalContext; const c = window._currentModalCases[idx]; if (!c) return; // Update Sidebar Active State document.querySelectorAll('.modal-sidebar-item').forEach(el => el.classList.remove('active')); const activeEl = document.getElementById(`sidebar-item-${idx}`); if (activeEl) activeEl.classList.add('active'); const p = c._actualPlatform || window._currentModalPlatform; const platCode = p.replace('case_', ''); const cId = c.case_id || (c._raw && c._raw.case_id) || (c.post && c.post.channel_content_id) || `temp_${idx}`; const cUrl = c.source_url || c.url || (c.post && c.post.link) || ''; const mappedS = ctx.sourceMap[cId] || ctx.sourceMap[cUrl] || (c._raw && ctx.sourceMap[c._raw.case_id]); const s = mappedS || c; const post = s.post || s || {}; const platformName = s.platform || (s._raw && s._raw.platform) || platCode; const title = post.title || c.title || '无标题'; const workflowUrl = s.source_url || s.url || cUrl; const publishedTime = post.publish_timestamp || post.published_at || '-'; const likeCount = post.like_count !== undefined ? post.like_count : (post.likes !== undefined ? post.likes : '-'); const collectCount = post.collect_count !== undefined ? post.collect_count : (post.collects !== undefined ? post.collects : '-'); const commentCount = post.comment_count !== undefined ? post.comment_count : (post.comments !== undefined ? post.comments : '-'); const shareCount = post.share_count !== undefined ? post.share_count : (post.shares !== undefined ? post.shares : '-'); const isFiltered = (c._actualPlatform === 'filtered_cases'); let filterActionHtml = ''; if (isFiltered) { filterActionHtml = ``; } else { filterActionHtml = ``; } const headerHtml = `

${title}

${filterActionHtml}
${workflowUrl ? `原文 ↗` : ''} 平台: ${platformName}
Published ${publishedTime}
Likes ${likeCount}
Collects ${collectCount}
Comments ${commentCount}
Shares ${shareCount}
`; document.getElementById('modal-main-header').innerHTML = headerHtml; // Media & Body let mediaHtml = ''; const images = post.images || []; const xImages = post.image_url_list || []; const ytThumbnails = post.thumbnails && post.thumbnails.length > 0 ? [post.thumbnails[post.thumbnails.length - 1].url] : []; const allImages = [...images, ...xImages.map(img => img.image_url), ...ytThumbnails].filter(Boolean); if (allImages.length > 0) { const lightboxArr = allImages.map((imgUrl, i) => ({ local: `/output/${ctx.reqId}/raw_cases/images/${cId}/${String(i).padStart(2, '0')}.jpg`, remote: imgUrl })); const lightboxStr = JSON.stringify(lightboxArr).replace(/"/g, '"'); mediaHtml += ``; } const videos = post.videos || []; const xVideos = post.video_url_list || []; const allVideos = [...videos, ...xVideos.map(vid => vid.video_url)].filter(Boolean); if (allVideos.length > 0) { mediaHtml += `
`; allVideos.forEach(vidUrl => { mediaHtml += ``; }); mediaHtml += `
`; } const bodyText = post.body_text || post.body || ''; // Source Panel let mainScrollableHtml = `
原始内容 / SOURCE POSTS 点击展开/折叠
${mediaHtml} ${bodyText ? `
${bodyText}
` : '
无正文
'}
`; // Evaluation Panel if (s.evaluation && Object.keys(s.evaluation).length > 0) { const renderEvalNode = (node, indent = 0) => { let html = ''; if (typeof node === 'object' && node !== null) { Object.entries(node).forEach(([k, v]) => { html += `
${k.replace(/_/g, ' ')}`; if (typeof v === 'object' && v !== null) { html += `
${renderEvalNode(v, 0)}
`; } else { const valColor = typeof v === 'number' ? '#3b82f6' : 'var(--text-main)'; html += `${String(v).replace(//g, '>')}`; } html += `
`; }); } return html; }; mainScrollableHtml += `

📊 质量评估 (Evaluation)

${renderEvalNode(s.evaluation)}
`; } // Extracted Data const wf = ctx.detailMap[cId] || (workflowUrl ? ctx.detailMapByUrl[workflowUrl] : null) || c; const detailedCaseObj = ctx.rawCasesObj['case'] || ctx.rawCasesObj['case_detailed']; const caseJsonCases = (detailedCaseObj && detailedCaseObj.cases) || []; const realCaseIndex = caseJsonCases.findIndex(jc => (jc.case_id === cId) || (jc._raw && jc._raw.case_id === cId) || (jc.post && jc.post.channel_content_id === cId) ); const caseIndexToPass = realCaseIndex >= 0 ? (caseJsonCases[realCaseIndex].index || (realCaseIndex + 1)) : -1; const btnWorkflowHtml = caseIndexToPass !== -1 ? `` : ''; const btnCapabilityHtml = caseIndexToPass !== -1 ? `` : ''; mainScrollableHtml += `

提取的工序 (Strategy)

${btnWorkflowHtml}

提取的能力 (Capability)

${btnCapabilityHtml}
`; document.getElementById('modal-main-scrollable').innerHTML = mainScrollableHtml; }; window.switchDetailTab = function (tabId) { document.querySelectorAll('.detail-tab-btn').forEach(btn => btn.classList.remove('active')); document.querySelectorAll('.detail-tab-content').forEach(content => content.style.display = 'none'); document.getElementById(`tab-btn-${tabId}`).classList.add('active'); document.getElementById(`tab-content-${tabId}`).style.display = 'block'; }; window.renderStructuredData = function (items, type, parentItem = null) { if (!items || items.length === 0) { return `
暂无${type === 'workflow' ? '工序' : '能力'}数据
`; } const formatIOs = (ios) => { if (!ios || !Array.isArray(ios) || ios.length === 0) return ''; const escapeHtml = (s) => String(s).replace(//g, '>'); return ios.map(io => { const desc = escapeHtml(io.description || io.role || '未知'); const mod = escapeHtml(io.modality || '未知'); return `${desc}[${mod}]`; }).join(' + '); }; const buildFullTitle = (inputs, outputs, actionStr, fallbackTitle) => { const escapeHtml = (s) => String(s).replace(//g, '>'); const inStr = formatIOs(inputs) || '无'; const outStr = formatIOs(outputs) || '无'; let parts = []; parts.push(inStr); if (actionStr) parts.push(`${escapeHtml(actionStr)}`); parts.push(outStr); return parts.join(' ➔ '); }; let html = ''; items.forEach((item, idx) => { const scopeId = 'scope_' + Math.random().toString(36).substr(2, 9); let title = ''; const hasValidIO = (arr) => Array.isArray(arr) && arr.length > 0 && (arr[0].role || arr[0].description); if (hasValidIO(item.inputs) || hasValidIO(item.outputs) || (item.steps && item.steps.length > 0)) { let actionStr = ''; if (item.action && item.action.description) { actionStr = item.action.description; } else if (item.method && !item.method.includes('[')) { actionStr = item.method; } else if (item.steps && Array.isArray(item.steps)) { const hasAnyValidIO = item.steps.some(s => hasValidIO(s.inputs) || hasValidIO(s.outputs) || s.body); if (hasAnyValidIO) { actionStr = item.steps.map(s => { if (s.action && s.action.description) { return s.action.description; } if (s.body) return s.body.length > 20 ? s.body.substring(0, 20) + '...' : s.body; if (s.method) return s.method; if (s.phase) return s.phase; return '未知'; }).join(' ➔ '); } else { actionStr = item.method || item.name || type === 'workflow' ? '工作流' : `节点 ${idx + 1}`; } } if (hasValidIO(item.inputs) || hasValidIO(item.outputs)) { title = buildFullTitle(item.inputs, item.outputs, actionStr, item.method || item.name || `节点 ${idx + 1}`); } else { title = String(actionStr).replace(//g, '>'); } } else { const escapeHtml = (s) => String(s).replace(//g, '>'); title = escapeHtml(item.method || item.name || ''); if (!title && item.action && item.action.description) { title = escapeHtml(item.action.description); } if (!title && item.body) { const actText = item.body.length > 50 ? item.body.substring(0, 50) + '...' : item.body; title = escapeHtml(actText); } if (!title) { title = escapeHtml(type === 'workflow' ? '工作流' : `节点 ${idx + 1}`); } } if (type === 'workflow' && item.workflow_id) { title = `${title} ${String(item.workflow_id).replace(//g, '>')}`; } // Tree node tags (from apply_to keys) or unstructured_what fallback const getApplyToField = (it) => { if (it.apply_to_grounding) return { key: 'apply_to_grounding', val: it.apply_to_grounding, suggest: it.suggest_apply_to }; if (it.apply_to_draft) return { key: 'apply_to_draft', val: it.apply_to_draft, suggest: null }; if (it.apply_to) return { key: 'apply_to', val: it.apply_to, suggest: it.suggest_apply_to }; return null; }; const applyToData = getApplyToField(item); let treeNodeTags = ''; if (applyToData && typeof applyToData.val === 'object') { const allLeafs = []; Object.values(applyToData.val).forEach(v => { if (Array.isArray(v)) { v.forEach(pathObj => { let leaf = ''; if (typeof pathObj === 'object' && pathObj !== null) { if (pathObj.element) leaf = pathObj.element; else if (pathObj.category_path || pathObj.path) { leaf = (pathObj.category_path || pathObj.path).split('/').pop(); } } else { leaf = String(pathObj).split('/').pop(); } if (leaf) allLeafs.push(leaf); }); } }); const uniqueLeafs = [...new Set(allLeafs)]; const badgeStyle = 'background: #f8fafc; color: #475569; border: 1px solid #cbd5e1; border-radius: 12px; padding: 2px 10px; font-size: 0.85em; font-weight: normal; margin-right: 6px; display: inline-block; white-space: nowrap;'; treeNodeTags = uniqueLeafs.map(leaf => `${leaf.replace(//g, '>')}`).join(''); } else if (item.unstructured_what && Array.isArray(item.unstructured_what)) { const badgeStyle = 'background: #f8fafc; color: #475569; border: 1px solid #cbd5e1; border-radius: 12px; padding: 2px 10px; font-size: 0.85em; font-weight: normal; margin-right: 6px; display: inline-block; white-space: nowrap;'; treeNodeTags = item.unstructured_what.map(t => `${String(t).replace(//g, '>')}`).join(''); } html += `
${title}
${treeNodeTags}
`; const renderApplyToVal = (valObj, suggestApplyTo = null) => { if (!valObj || typeof valObj !== 'object') return '-'; const escapeApplyToText = (s) => String(s).replace(//g, '>'); const renderPathParts = (pathValue, highlight = false) => { const pathStr = String(pathValue || '').trim(); if (!pathStr) return ''; const parts = pathStr.split('/'); const leaf = parts.pop(); const prefix = parts.length > 0 ? parts.join('/') + '/' : ''; const leafStyle = highlight ? 'background:#eff6ff; color:#2563eb; border:1px solid #bfdbfe;' : ''; return ` ${prefix ? `${escapeApplyToText(prefix)}` : ''} ${escapeApplyToText(leaf)} `; }; const renderTooltip = (pathObj) => { if ( typeof pathObj !== 'object' || pathObj === null || !(pathObj.rationale || pathObj.body_excerpt || pathObj.body_excerpt_note || pathObj.body_excerpt_type || pathObj.category_id) ) { return ''; } return `
${pathObj.category_id ? `id: ${pathObj.category_id}` : ''} ${pathObj.rationale ? `${escapeApplyToText(pathObj.rationale)}` : ''} ${pathObj.body_excerpt ? `${escapeApplyToText(pathObj.body_excerpt)}` : ''} ${pathObj.body_excerpt_note ? `${escapeApplyToText(pathObj.body_excerpt_note)}` : ''} ${pathObj.body_excerpt_type ? `type: ${escapeApplyToText(pathObj.body_excerpt_type)}` : ''}
`; }; const renderEvidence = (pathObj) => { if (typeof pathObj !== 'object' || pathObj === null) return ''; if (!('body_excerpt' in pathObj) && !('body_excerpt_note' in pathObj) && !('body_excerpt_type' in pathObj)) return ''; const excerpt = pathObj.body_excerpt || ''; const note = pathObj.body_excerpt_note || ''; const excerptType = pathObj.body_excerpt_type || ''; const excerptKey = excerpt ? makeExcerptKey(excerpt) : ''; return `
关联做法 ${excerptType ? escapeApplyToText(excerptType) : 'type: 空'} ${note ? escapeApplyToText(note) : '空'}
`; }; let res = '
'; let hasRows = false; Object.entries(valObj).forEach(([k, v]) => { if (Array.isArray(v) && v.length > 0) { hasRows = true; res += `
${escapeApplyToText(k)}
`; v.forEach(pathObj => { let pathStr = ''; let elementStr = ''; if (typeof pathObj === 'object' && pathObj !== null) { pathStr = pathObj.category_path || pathObj.path || ''; elementStr = pathObj.element || ''; } else { pathStr = String(pathObj); } let tooltipHtml = renderTooltip(pathObj); let htmlParts = ''; if (pathStr && elementStr) { htmlParts = `${escapeApplyToText(pathStr)}${escapeApplyToText(elementStr)}`; } else if (pathStr) { htmlParts = renderPathParts(pathStr); } if (htmlParts) { const evidenceHtml = renderEvidence(pathObj); res += `${htmlParts}${tooltipHtml}${evidenceHtml}`; } }); res += `
`; } }); const suggestItems = Array.isArray(suggestApplyTo) ? suggestApplyTo : (typeof suggestApplyTo === 'string' && suggestApplyTo.trim() ? [{ path: suggestApplyTo }] : []); if (suggestItems.length > 0) { hasRows = true; res += `
建议
`; suggestItems.forEach(item => { const pathStr = typeof item === 'object' && item !== null ? item.path : String(item || ''); const tooltipHtml = renderTooltip(item); if (pathStr) { const evidenceHtml = renderEvidence(item); res += `${renderPathParts(pathStr, true)}${tooltipHtml}${evidenceHtml}`; } }); res += `
`; } res += `
`; return hasRows ? res : '-'; }; // Render apply_to / apply_to_grounding at workflow level (if it exists) if (applyToData && typeof applyToData.val === 'object' && Object.keys(applyToData.val).length > 0) { html += `
${applyToData.key}
${renderApplyToVal(applyToData.val, applyToData.suggest)}
`; } if (item.action && typeof item.action === 'object' && (item.action.description || item.action.reasoning)) { const actionDescription = item.action.description ? String(item.action.description).replace(//g, '>') : ''; const actionReasoning = item.action.reasoning ? String(item.action.reasoning).replace(//g, '>') : ''; html += `
action
${actionDescription ? ` ${actionDescription} ${actionReasoning ? `` : ''} ` : ''}
`; } // Stage rendering removed per request // Render effects if (item.effects && Array.isArray(item.effects) && item.effects.length > 0) { let effectsHtml = ''; item.effects.forEach(effectItem => { if (typeof effectItem === 'string') { effectsHtml += `
  • ${effectItem.replace(//g, '>')}
  • `; } else if (typeof effectItem === 'object' && effectItem !== null) { const stmt = effectItem.statement ? effectItem.statement.replace(//g, '>') : 'Effect'; let detailsHtml = ''; const excludeKeys = ['statement']; Object.entries(effectItem).forEach(([k, v]) => { if (!excludeKeys.includes(k) && v !== null && v !== undefined && v !== '' && (!Array.isArray(v) || v.length > 0)) { let valStr = ''; if (Array.isArray(v)) { valStr = ``; } else if (typeof v === 'object') { valStr = JSON.stringify(v); } else { valStr = String(v).replace(//g, '>'); } if (Array.isArray(v)) { detailsHtml += `
    ${k.replace(/_/g, ' ')}:
    ${valStr}
    `; } else { detailsHtml += `
    ${k.replace(/_/g, ' ')}: ${valStr}
    `; } } }); effectsHtml += `
  • ${stmt}
    ${detailsHtml}
  • `; } }); html += `
    effects
      ${effectsHtml}
    `; } // Render confidence fields const formatDate = (ts) => { if (!ts) return '-'; if (typeof ts === 'string' && ts.includes('-')) return ts; const num = Number(ts); if (isNaN(num) || num <= 0) return '-'; const d = new Date(num > 10000000000 ? num : num * 1000); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; }; html += `
    置信度
    Maturity: ${String((item.maturity || (parentItem && parentItem.maturity)) || '-').replace(//g, '>')}
    Validation: ${(item.validation_count !== undefined && item.validation_count !== null) ? String(item.validation_count).replace(//g, '>') : (parentItem && parentItem.validation_count !== undefined && parentItem.validation_count !== null) ? String(parentItem.validation_count).replace(//g, '>') : '-'}
    Published: ${formatDate(item.published_at || (parentItem && parentItem.published_at))}
    Last Verified: ${formatDate(item.last_verified_at || (parentItem && parentItem.last_verified_at))}
    Created: ${formatDate(item.created_at || (parentItem && parentItem.created_at))}
    Updated: ${formatDate(item.updated_at || (parentItem && parentItem.updated_at))}
    `; // Render feedback if available const feedbackVal = item.feedback || (parentItem && parentItem.feedback); if (feedbackVal) { let feedbackHtml = ''; if (typeof feedbackVal === 'object' && feedbackVal !== null) { Object.entries(feedbackVal).forEach(([k, v]) => { if (v !== null && v !== undefined && String(v).trim() !== '') { feedbackHtml += `
    ${k}: ${v}
    `; } }); if (feedbackHtml !== '') { feedbackHtml = `
    ${feedbackHtml}
    `; } } else if (typeof feedbackVal === 'string' && feedbackVal.trim() !== '') { feedbackHtml = `
    ${String(feedbackVal).replace(//g, '>')}
    `; } if (feedbackHtml !== '') { html += `
    feedback
    ${feedbackHtml}
    `; } } // Render body if (item.body && typeof item.body === 'string') { html += `
    body
    ${item.body.replace(//g, '>')}
    `; } // Helper for inputs/outputs (Moved up so it can be used by steps) const renderDataObjList = (list) => { const isValid = (v) => v !== null && v !== undefined && String(v).toLowerCase() !== 'null' && String(v).toLowerCase() !== 'none' && String(v).trim() !== ''; const escapeHtml = (s) => String(s).replace(//g, '>'); return list.map(io => { if (!io) return ''; if (typeof io === 'string') return `
    ${escapeHtml(io)}
    `; const desc = isValid(io.description) ? io.description.replace(//g, '>') : ''; const mod = isValid(io.modality) ? io.modality : ''; const relation = isValid(io.relation) ? io.relation.replace(//g, '>') : ''; let modalityIcon = ''; let modBorder = '#e2e8f0'; let modColor = '#64748b'; let modBg = '#ffffff'; if (mod) { if (mod === '文本') { modalityIcon = 'T'; } else if (mod === '图片') { modalityIcon = '🖼️'; modColor = '#10b981'; } else if (mod === '视频') { modalityIcon = '▶️'; modColor = '#ef4444'; } else if (mod === '音频') { modalityIcon = '🎵'; modColor = '#ec4899'; } else if (mod === '参数') { modalityIcon = '⚙️'; modBg = '#f8fafc'; } else { modalityIcon = escapeHtml(mod[0] || '?'); } } const modalityHtml = mod ? `${modalityIcon}` : ''; let relationHtml = ''; if (relation) { const relStr = String(relation); const relMatch = relStr.match(/\[(来源|去向)\.(.+)\]/); if (relMatch) { const dir = relMatch[1]; const target = relMatch[2]; let targetLabel = target; let targetStepId = ''; let targetDirection = ''; let badgeBg = '#eff6ff'; // blue-50 let badgeColor = '#3b82f6'; // blue-500 if (target === '原始输入') { targetLabel = '初始 ①'; badgeBg = '#eef2ff'; // indigo-50 badgeColor = '#6366f1'; // indigo-500 } else if (target === '最终输出') { targetLabel = '最终输出'; badgeBg = '#f0fdfa'; // teal-50 badgeColor = '#14b8a6'; // teal-500 } else if (target.startsWith('s') && target.length >= 3) { const stepNumMatch = target.match(/s(\d+)([IO]?)/); if (stepNumMatch) { targetLabel = `步骤${stepNumMatch[1]}`; targetStepId = `s${stepNumMatch[1]}`; targetDirection = stepNumMatch[2] === 'I' ? 'input' : (stepNumMatch[2] === 'O' ? 'output' : ''); } } let clickAttr = targetStepId ? `onclick="event.stopPropagation(); window.jumpToWorkflowStep('${scopeId}', '${targetStepId}', '${targetDirection}')" onmouseenter="this.style.filter='brightness(0.95)'; window.hoverWorkflowStep('${scopeId}', '${targetStepId}', '${targetDirection}')" onmouseleave="this.style.filter='none'; window.unhoverWorkflowStep('${scopeId}', '${targetStepId}', '${targetDirection}')" style="cursor:pointer; ` : `onmouseenter="this.style.filter='brightness(0.95)'" onmouseleave="this.style.filter='none'" style="`; relationHtml = `${targetLabel}`; } else { relationHtml = `${escapeHtml(relation)}`; } } let content = desc; if (!content) { const keys = Object.keys(io).filter(k => k !== 'modality' && k !== 'relation'); if (keys.length > 0) { content = keys.map(k => `${k}${io[k]}`).join(' '); } } return `
    ${modalityHtml}${relationHtml}${content}
    `; }).join(''); }; // Render steps array specially if (item.steps && Array.isArray(item.steps)) { const allCapabilities = (item && item.capability) || (parentItem && parentItem.capability) || []; const escapeHtml = (s) => String(s).replace(//g, '>'); const minWidth = 1250; const renderAction = (src) => { if (!src) return '-'; if (src.action && src.action.description) { const description = escapeHtml(src.action.description); const reasoning = src.action.reasoning ? escapeHtml(src.action.reasoning) : ''; return ` ${description} ${reasoning ? `${reasoning}` : ''} `; } if (src.method) return escapeHtml(src.method); if (src.description) return escapeHtml(src.description); return '-'; }; const renderTools = (tools) => { if (!tools || !Array.isArray(tools) || tools.length === 0) return '-'; return tools.map(t => `${escapeHtml(t)}`).join(''); }; const renderEffects = (effects) => { if (!effects || !Array.isArray(effects) || effects.length === 0) return '-'; const renderKeyTag = (keyText) => `${keyText}`; return `
    ${effects.map(effect => { if (typeof effect === 'string') { return `
    ${renderKeyTag('效果')}${escapeHtml(effect)}
    `; } if (typeof effect !== 'object' || effect === null) return ''; const statement = effect.statement ? escapeHtml(effect.statement) : '效果'; const criteria = effect.criteria ? escapeHtml(effect.criteria) : ''; const judgeMethod = effect.judge_method ? escapeHtml(effect.judge_method) : ''; const negativeExamples = Array.isArray(effect.negative_examples) && effect.negative_examples.length > 0 ? `
    ` + effect.negative_examples.map(ex => `${escapeHtml(ex)}`).join('') + `
    ` : ''; return `
    ${statement}
    ${criteria ? `
    ${renderKeyTag('判断标准')}${criteria}
    ` : ''} ${judgeMethod ? `
    ${renderKeyTag('评判方式')}${judgeMethod}
    ` : ''} ${negativeExamples ? `
    ${renderKeyTag('负面示例')}${negativeExamples}
    ` : ''}
    `; }).join('')}
    `; }; const getStepCapabilities = (step) => { if (!step || !step.step_id) return []; const workflowId = item && item.workflow_id; return allCapabilities.filter(capability => { const ref = capability.workflow_step_ref || {}; const refStepId = ref.step_id; const refWorkflowId = ref.workflow_id; if (refStepId === step.step_id && (!workflowId || !refWorkflowId || refWorkflowId === workflowId)) { return true; } return capability.capability_id && new RegExp(`^c_${workflowId || 'w[0-9]+'}_${step.step_id}_[0-9]+$`).test(capability.capability_id); }); }; const matchedCapabilities = new Set(); const collectBodyExcerpts = (applyTo, suggestApplyTo) => { const excerpts = []; const addExcerpt = (entry) => { if (entry && typeof entry === 'object' && typeof entry.body_excerpt === 'string' && entry.body_excerpt.trim()) { excerpts.push(entry.body_excerpt.trim()); } }; if (applyTo && typeof applyTo === 'object') { Object.values(applyTo).forEach(items => { if (Array.isArray(items)) items.forEach(addExcerpt); }); } if (Array.isArray(suggestApplyTo)) { suggestApplyTo.forEach(addExcerpt); } return [...new Set(excerpts)].sort((a, b) => b.length - a.length); }; const renderBodyWithExcerptHighlights = (body, applyTo, suggestApplyTo) => { if (!body) return '-'; const text = String(body); const excerpts = collectBodyExcerpts(applyTo, suggestApplyTo); if (excerpts.length === 0) return escapeHtml(text); let out = ''; let i = 0; while (i < text.length) { const match = excerpts.find(excerpt => text.startsWith(excerpt, i)); if (match) { out += `${escapeHtml(match)}`; i += match.length; } else { out += escapeHtml(text[i]); i += 1; } } return out; }; const renderCapabilityColumns = (capability, step) => { const applyTo = capability && (capability.apply_to_draft || capability.apply_to_grounding || capability.apply_to); const suggestApplyTo = capability && capability.apply_to_draft ? null : capability && capability.suggest_apply_to; const bodyHtml = capability && capability.body ? renderBodyWithExcerptHighlights(capability.body, applyTo, suggestApplyTo) : '-'; return ` ${capability && capability.capability_id ? `${escapeHtml(capability.capability_id)}` : '-'} ${capability && capability.inputs && capability.inputs.length > 0 ? renderDataObjList(capability.inputs) : '-'} ${renderAction(capability)} ${capability && capability.outputs && capability.outputs.length > 0 ? renderDataObjList(capability.outputs) : '-'}
    ${applyTo ? renderApplyToVal(applyTo, suggestApplyTo) : '-'}
    ${bodyHtml}
    ${capability ? renderEffects(capability.effects) : '-'}
    ${capability ? renderTools(capability.tools) : '-'}
    `; }; html += `
    steps
    `; const CIRCLE_NUMS = ['①','②','③','④','⑤','⑥','⑦','⑧','⑨','⑩']; const ROW_COLORS = ['#4f46e5', '#0d9488', '#e11d48', '#7c3aed', '#0284c7', '#d97706', '#65a30d', '#db2777']; const stepSourceMap = {}; let initCount = 0; // First pass: collect outputs and init inputs item.steps.forEach((step, sIdx) => { if (step.outputs) { step.outputs.forEach((out, outIdx) => { if (out.id) { stepSourceMap[out.id] = { step_id: step.step_id, order: sIdx, index: outIdx, type: out.type, color: ROW_COLORS[sIdx % ROW_COLORS.length], hasMultiple: step.outputs.length > 1 }; } }); } if (step.inputs) { step.inputs.forEach(inp => { if (inp.source_id && inp.source_id.startsWith('init_input')) { if (!stepSourceMap[inp.source_id]) { stepSourceMap[inp.source_id] = { isInit: true, order: initCount++ }; } } }); } }); const getModalityHtml = (mod) => { let modalityIcon = escapeHtml(mod ? mod[0] : '?'); let modBorder = '#e2e8f0'; let modColor = '#64748b'; let modBg = '#ffffff'; if (mod === '文本') { modalityIcon = 'T'; modColor = '#3b82f6'; modBg = '#eff6ff'; modBorder = '#bfdbfe'; } else if (mod === '图片') { modalityIcon = '🖼️'; modColor = '#10b981'; modBg = '#ecfdf5'; modBorder = '#a7f3d0'; } else if (mod === '视频' || mod === '视频片断') { modalityIcon = '▶️'; modColor = '#ef4444'; modBg = '#fef2f2'; modBorder = '#fecaca'; } else if (mod === '音频') { modalityIcon = '🎵'; modColor = '#ec4899'; modBg = '#fdf2f8'; modBorder = '#fbcfe8'; } else if (mod === '图片内容组') { modalityIcon = '🖼️'; modColor = '#10b981'; modBg = '#ecfdf5'; modBorder = '#a7f3d0'; } return `${modalityIcon}`; }; item.steps.forEach((step, stepIdx) => { const sColor = ROW_COLORS[stepIdx % ROW_COLORS.length]; const inputs = step.inputs || []; const outputs = step.outputs || []; const maxRows = Math.max(inputs.length, outputs.length, 1); const hoverAttr = `onmouseenter="this.style.background='rgba(0,0,0,0.03)';" onmouseleave="this.style.background='white';"`; for (let i = 0; i < maxRows; i++) { const rowBorderBottom = (i === maxRows - 1) ? `2px solid ${sColor}` : `1px dashed #e5e7eb`; html += ``; if (i === 0) { const renderTagList = (items, cssClass) => { if (!items || !Array.isArray(items) || items.length === 0) return ''; const tags = items.map(t => `${escapeHtml(t)}`).join(''); return `
    ${tags}
    `; }; const rolesHtml = step.roles ? renderTagList(step.roles, 'tag-role') : (step.stage ? `
    ${escapeHtml(step.stage)}
    ` : ''); const actionsHtml = step.actions ? renderTagList(step.actions, 'tag-action') : ''; const purposesHtml = step.purposes ? renderTagList(step.purposes, 'tag-purpose') : ''; const stepIdAttr = `id="step-s${escapeHtml(step.step_id || '')}"`; html += ` `; } const inp = inputs[i]; if (inp) { // resolve modality/category based on source const sInfo = stepSourceMap[inp.source_id]; let modalityHtml = getModalityHtml(inp.modality || inp.type); let categoryHtml = inp.category ? `${escapeHtml(inp.category)}` : ''; let sourceHtml = escapeHtml(inp.source_id || '-'); if (sInfo) { if (sInfo.isInit) { sourceHtml = `初始${CIRCLE_NUMS[sInfo.order % 10]}`; } else { const bdColor = sInfo.color + '40'; const clickAttr = `onclick="event.stopPropagation(); window.jumpToWorkflowStep('${scopeId}', 's${sInfo.step_id}', ${sInfo.index})" onmouseenter="window.hoverWorkflowStep('${scopeId}', 's${sInfo.step_id}', ${sInfo.index})" onmouseleave="window.unhoverWorkflowStep('${scopeId}', 's${sInfo.step_id}', ${sInfo.index})"`; const circleHtml = sInfo.hasMultiple ? `${CIRCLE_NUMS[sInfo.index % 10]}` : ''; sourceHtml = `步骤${sInfo.step_id}${circleHtml}`; // Source dictates modality & category overrides if available const sourceOutputObj = item.steps[sInfo.order] && item.steps[sInfo.order].outputs && item.steps[sInfo.order].outputs[sInfo.index]; if (sourceOutputObj) { modalityHtml = getModalityHtml(sourceOutputObj.modality || sourceOutputObj.type || inp.modality || inp.type); if (sourceOutputObj.category) { categoryHtml = `${escapeHtml(sourceOutputObj.category)}`; } } } } html += ` `; } else { html += ``; } if (i === 0) { let toolHtml = ''; if (step.tool_name || step.tool_method) { let parts = []; if (step.tool_name) parts.push(`
    ${escapeHtml(step.tool_name)}
    `); if (step.tool_method) parts.push(`
    ${escapeHtml(step.tool_method)}
    `); toolHtml = parts.join(''); } else if (step.tool) { toolHtml = `
    ${escapeHtml(step.tool)}
    `; } html += ``; } const out = outputs[i]; if (out) { const typeHtml = getModalityHtml(out.modality || out.type); let categoryHtml = out.category ? `${escapeHtml(out.category)}` : ''; const cNum = CIRCLE_NUMS[i % 10]; html += ` `; } else { html += ``; } if (i === 0) { html += ``; } html += ``; } }); html += `
    Id 步骤名称 动作 角色 目的 步骤输入 工具 步骤输出 来源
    模态 领域类型 来源 value # 模态 领域类型 value
    ${escapeHtml(step.step_id || (stepIdx + 1))}
    ${escapeHtml(step.name || '')}
    ${actionsHtml} ${rolesHtml} ${purposesHtml}${modalityHtml} ${categoryHtml} ${sourceHtml} ${inp.value ? escapeHtml(inp.value) : ''}${toolHtml}${cNum} ${typeHtml} ${categoryHtml} ${out.value ? escapeHtml(out.value) : ''}${escapeHtml(step.source_ref || '')}
    `; } // Render inputs if (item.inputs && Array.isArray(item.inputs) && item.inputs.length > 0) { html += `
    inputs
    ${renderDataObjList(item.inputs)}
    `; } else if (item.inputs && typeof item.inputs === 'object' && Object.keys(item.inputs).length > 0 && !Array.isArray(item.inputs)) { // Fallback for old schema html += `
    inputs
    `; Object.entries(item.inputs).forEach(([k, v]) => { html += `
    ${k}${v}
    `; }); html += `
    `; } // Render outputs if (item.outputs && Array.isArray(item.outputs) && item.outputs.length > 0) { html += `
    outputs
    ${renderDataObjList(item.outputs)}
    `; } else if (item.outputs && typeof item.outputs === 'object' && Object.keys(item.outputs).length > 0 && !Array.isArray(item.outputs)) { // Fallback for old schema html += `
    outputs
    `; Object.entries(item.outputs).forEach(([k, v]) => { html += `
    ${k}${v}
    `; }); html += `
    `; } // Render tools (for non-step items like capabilities) if (item.tools && Array.isArray(item.tools) && item.tools.length > 0) { html += `
    tools
    ${item.tools.map(t => `${t}`).join('')}
    `; } // Dynamic fallback for any other unhandled keys const handledKeys = [ 'method', 'name', 'action', 'unstructured_what', 'apply_to_grounding', 'apply_to_draft', 'apply_to', 'suggest_apply_to', 'stage', 'effects', 'body', 'steps', 'inputs', 'outputs', 'tools', 'workflow_id', 'capability', 'source', 'channel_content_id' ]; Object.keys(item).forEach(k => { if (!handledKeys.includes(k)) { let v = item[k]; if (v === null || v === undefined || v === '') return; let displayHtml = ''; if (typeof v === 'object') { if (Array.isArray(v)) { if (v.length === 0) return; if (typeof v[0] === 'object') { displayHtml = `
    ${JSON.stringify(v, null, 2).replace(//g, '>')}
    `; } else { displayHtml = v.map(vi => `${String(vi).replace(//g, '>')}`).join(''); } } else { if (Object.keys(v).length === 0) return; displayHtml = `
    ${JSON.stringify(v, null, 2).replace(//g, '>')}
    `; } } else { displayHtml = String(v).replace(//g, '>'); } html += `
    ${k}
    ${displayHtml}
    `; } }); html += `
    `; }); return html; };