|
|
class AgenticCell extends HTMLElement { |
|
|
constructor() { |
|
|
super(); |
|
|
this.attachShadow({ mode: 'open' }); |
|
|
} |
|
|
|
|
|
static get observedAttributes() { |
|
|
return ['agent-type', 'input', 'output', 'status', 'timestamp', 'has-previous']; |
|
|
} |
|
|
|
|
|
attributeChangedCallback(name, oldValue, newValue) { |
|
|
if (oldValue !== newValue) { |
|
|
this.render(); |
|
|
} |
|
|
} |
|
|
|
|
|
render() { |
|
|
const agentType = this.getAttribute('agent-type') || 'researcher'; |
|
|
const input = this.getAttribute('input') || ''; |
|
|
const output = this.getAttribute('output') ? JSON.parse(this.getAttribute('output')) : null; |
|
|
const status = this.getAttribute('status') || 'idle'; |
|
|
const timestamp = this.getAttribute('timestamp') || new Date().toISOString(); |
|
|
const hasPrevious = this.hasAttribute('has-previous'); |
|
|
|
|
|
const agentIcons = { |
|
|
'researcher': 'search', |
|
|
'analyst': 'bar-chart-2', |
|
|
'data-visualizer': 'pie-chart', |
|
|
'summarizer': 'file-text' |
|
|
}; |
|
|
|
|
|
const statusColors = { |
|
|
'idle': 'bg-gray-200', |
|
|
'loading': 'bg-blue-200 animate-pulse-slow', |
|
|
'success': 'bg-green-200', |
|
|
'error': 'bg-red-200' |
|
|
}; |
|
|
|
|
|
this.shadowRoot.innerHTML = ` |
|
|
<style> |
|
|
:host { |
|
|
display: block; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.tool-badge { |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
padding: 0.25rem 0.5rem; |
|
|
margin-right: 0.5rem; |
|
|
margin-bottom: 0.5rem; |
|
|
font-size: 0.75rem; |
|
|
border-radius: 0.25rem; |
|
|
background-color: #f3f4f6; |
|
|
color: #4b5563; |
|
|
} |
|
|
|
|
|
.checkpoint-badge { |
|
|
position: absolute; |
|
|
top: 0.5rem; |
|
|
right: 0.5rem; |
|
|
padding: 0.25rem 0.5rem; |
|
|
border-radius: 0.25rem; |
|
|
font-size: 0.75rem; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.checkpoint-pending { |
|
|
background-color: #fef3c7; |
|
|
color: #92400e; |
|
|
} |
|
|
|
|
|
.checkpoint-approved { |
|
|
background-color: #d1fae5; |
|
|
color: #065f46; |
|
|
} |
|
|
|
|
|
.checkpoint-rejected { |
|
|
background-color: #fee2e2; |
|
|
color: #991b1b; |
|
|
} |
|
|
|
|
|
.provenance-panel { |
|
|
margin-top: 1rem; |
|
|
padding: 0.75rem; |
|
|
background-color: #f9fafb; |
|
|
border-radius: 0.375rem; |
|
|
border: 1px solid #e5e7eb; |
|
|
} |
|
|
|
|
|
.provenance-header { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
cursor: pointer; |
|
|
font-weight: 500; |
|
|
color: #4b5563; |
|
|
} |
|
|
|
|
|
.provenance-content { |
|
|
margin-top: 0.5rem; |
|
|
padding-top: 0.5rem; |
|
|
border-top: 1px solid #e5e7eb; |
|
|
} |
|
|
|
|
|
.citation-item { |
|
|
margin-bottom: 0.5rem; |
|
|
padding: 0.5rem; |
|
|
background-color: white; |
|
|
border-radius: 0.25rem; |
|
|
border: 1px solid #e5e7eb; |
|
|
} |
|
|
|
|
|
.verification-badge { |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
padding: 0.25rem 0.5rem; |
|
|
border-radius: 0.25rem; |
|
|
font-size: 0.75rem; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.verification-high { |
|
|
background-color: #d1fae5; |
|
|
color: #065f46; |
|
|
} |
|
|
|
|
|
.verification-medium { |
|
|
background-color: #fef3c7; |
|
|
color: #92400e; |
|
|
} |
|
|
|
|
|
.verification-low { |
|
|
background-color: #fee2e2; |
|
|
color: #991b1b; |
|
|
} |
|
|
.cell-container { |
|
|
border-radius: 0.5rem; |
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
|
background-color: white; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.cell-header { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
padding: 0.75rem 1rem; |
|
|
background-color: #f9fafb; |
|
|
border-bottom: 1px solid #e5e7eb; |
|
|
} |
|
|
|
|
|
.agent-icon { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
width: 2rem; |
|
|
height: 2rem; |
|
|
border-radius: 50%; |
|
|
margin-right: 0.75rem; |
|
|
} |
|
|
|
|
|
.agent-name { |
|
|
font-weight: 600; |
|
|
color: #374151; |
|
|
text-transform: capitalize; |
|
|
} |
|
|
|
|
|
.cell-status { |
|
|
margin-left: auto; |
|
|
font-size: 0.75rem; |
|
|
padding: 0.25rem 0.5rem; |
|
|
border-radius: 9999px; |
|
|
} |
|
|
|
|
|
.cell-timestamp { |
|
|
margin-left: 0.75rem; |
|
|
font-size: 0.75rem; |
|
|
color: #6b7280; |
|
|
} |
|
|
|
|
|
.cell-content { |
|
|
padding: 1rem; |
|
|
} |
|
|
|
|
|
.input-area { |
|
|
width: 100%; |
|
|
min-height: 3rem; |
|
|
padding: 0.75rem; |
|
|
border: 1px solid #e5e7eb; |
|
|
border-radius: 0.375rem; |
|
|
margin-bottom: 1rem; |
|
|
font-family: inherit; |
|
|
} |
|
|
|
|
|
.input-area:focus { |
|
|
outline: none; |
|
|
border-color: #3b82f6; |
|
|
box-shadow: 0 0 0 1px #3b82f6; |
|
|
} |
|
|
|
|
|
.actions { |
|
|
display: flex; |
|
|
justify-content: flex-end; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
.run-btn { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
padding: 0.5rem 1rem; |
|
|
background-color: #3b82f6; |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 0.375rem; |
|
|
cursor: pointer; |
|
|
transition: background-color 0.2s; |
|
|
} |
|
|
|
|
|
.run-btn:hover { |
|
|
background-color: #2563eb; |
|
|
} |
|
|
|
|
|
.run-btn:disabled { |
|
|
background-color: #9ca3af; |
|
|
cursor: not-allowed; |
|
|
} |
|
|
|
|
|
.output-container { |
|
|
border-top: 1px solid #e5e7eb; |
|
|
padding-top: 1rem; |
|
|
} |
|
|
|
|
|
.output-header { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
margin-bottom: 0.75rem; |
|
|
color: #4b5563; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.empty-output { |
|
|
color: #9ca3af; |
|
|
font-style: italic; |
|
|
padding: 1rem; |
|
|
text-align: center; |
|
|
} |
|
|
</style> |
|
|
|
|
|
<div class="cell-container cell-connector"> |
|
|
<div class="cell-header"> |
|
|
<div class="agent-icon bg-blue-100 text-blue-600"> |
|
|
<i data-feather="${agentIcons[agentType] || 'circle'}"></i> |
|
|
</div> |
|
|
<span class="agent-name">${agentType}</span> |
|
|
${status !== 'idle' ? ` |
|
|
<span class="cell-status ${statusColors[status]}"> |
|
|
${status === 'loading' ? 'Running...' : status} |
|
|
</span> |
|
|
<span class="cell-timestamp">${new Date(timestamp).toLocaleString()}</span> |
|
|
` : ''} |
|
|
</div> |
|
|
${output?.metadata?.requires_human_check ? ` |
|
|
<div class="checkpoint-badge checkpoint-${output.metadata.human_check_status || 'pending'}"> |
|
|
${output.metadata.human_check_status === 'approved' ? '✅ Approved' : |
|
|
output.metadata.human_check_status === 'rejected' ? '❌ Rejected' : '🧍 Pending Review'} |
|
|
</div> |
|
|
` : ''} |
|
|
<div class="cell-content"> |
|
|
<textarea class="input-area" placeholder="Enter your prompt or reference other cells with {{cell_id.output}}">${input}</textarea> |
|
|
|
|
|
<div class="actions"> |
|
|
<button class="run-btn" ${status === 'loading' ? 'disabled' : ''}> |
|
|
<i data-feather="play"></i> |
|
|
Run |
|
|
</button> |
|
|
</div> |
|
|
<div class="output-container"> |
|
|
${output ? ` |
|
|
<div class="output-header"> |
|
|
<i data-feather="file-text" class="mr-2"></i> |
|
|
Output |
|
|
</div> |
|
|
${output.tools_used ? ` |
|
|
<div class="mt-2 mb-3"> |
|
|
<div class="text-xs text-gray-500 mb-1">Tools used:</div> |
|
|
${output.tools_used.map(tool => ` |
|
|
<span class="tool-badge"> |
|
|
<i data-feather="${tool.icon || 'tool'}" class="w-3 h-3 mr-1"></i> |
|
|
${tool.name} |
|
|
</span> |
|
|
`).join('')} |
|
|
</div> |
|
|
` : ''} |
|
|
<agentic-output-viewer output='${JSON.stringify(output)}'></agentic-output-viewer> |
|
|
|
|
|
${output.metadata ? ` |
|
|
<div class="provenance-panel"> |
|
|
<div class="provenance-header" onclick="this.nextElementSibling.classList.toggle('hidden')"> |
|
|
<i data-feather="info" class="mr-2"></i> |
|
|
Provenance & Metadata |
|
|
<i data-feather="chevron-down" class="ml-auto w-4 h-4"></i> |
|
|
</div> |
|
|
<div class="provenance-content hidden"> |
|
|
${output.metadata.version_hash ? ` |
|
|
<div class="mb-2"> |
|
|
<div class="text-xs text-gray-500">Version:</div> |
|
|
<div class="flex items-center mt-1"> |
|
|
<code class="text-xs bg-gray-100 p-1 rounded mr-2">${output.metadata.version_hash.substring(0, 12)}...</code> |
|
|
<button class="text-xs text-blue-600 hover:text-blue-800" onclick="navigator.clipboard.writeText('${output.metadata.version_hash}')">Copy</button> |
|
|
</div> |
|
|
</div> |
|
|
` : ''} |
|
|
|
|
|
${output.metadata.verification_score ? ` |
|
|
<div class="mb-2"> |
|
|
<div class="text-xs text-gray-500">Verification:</div> |
|
|
<div class="mt-1"> |
|
|
<span class="verification-badge ${output.metadata.verification_score > 0.8 ? 'verification-high' : |
|
|
output.metadata.verification_score > 0.5 ? 'verification-medium' : 'verification-low'}"> |
|
|
${output.metadata.verification_score > 0.8 ? '✅ High' : |
|
|
output.metadata.verification_score > 0.5 ? '⚠️ Medium' : '❌ Low'} (${Math.round(output.metadata.verification_score * 100)}%) |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
` : ''} |
|
|
|
|
|
${output.metadata.source_citations?.length > 0 ? ` |
|
|
<div> |
|
|
<div class="text-xs text-gray-500">Citations:</div> |
|
|
<div class="mt-1"> |
|
|
${output.metadata.source_citations.map(cite => ` |
|
|
<div class="citation-item"> |
|
|
<a href="${cite.url}" target="_blank" class="text-sm text-blue-600 hover:underline">${cite.title}</a> |
|
|
${cite.description ? `<div class="text-xs text-gray-500 mt-1">${cite.description}</div>` : ''} |
|
|
</div> |
|
|
`).join('')} |
|
|
</div> |
|
|
</div> |
|
|
` : ''} |
|
|
</div> |
|
|
</div> |
|
|
` : ''} |
|
|
` : ` |
|
|
<div class="empty-output">No output yet. Run the cell to see results.</div> |
|
|
`} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
const runBtn = this.shadowRoot.querySelector('.run-btn'); |
|
|
runBtn.addEventListener('click', () => { |
|
|
const cellId = this.getAttribute('id'); |
|
|
mockRunCell(cellId); |
|
|
}); |
|
|
|
|
|
|
|
|
if (window.feather) { |
|
|
window.feather.replace(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
customElements.define('agentic-cell', AgenticCell); |