Initial commit
This commit is contained in:
144
dashboard/js/components/session-inspector.js
Normal file
144
dashboard/js/components/session-inspector.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Session Inspector — right panel with full state rendering
|
||||
*/
|
||||
|
||||
let inspectorEl;
|
||||
|
||||
export function initInspector() {
|
||||
inspectorEl = document.getElementById('inspector');
|
||||
clearInspector();
|
||||
}
|
||||
|
||||
export function clearInspector() {
|
||||
if (!inspectorEl) return;
|
||||
inspectorEl.innerHTML = '<div class="inspector-empty">No session selected</div>';
|
||||
}
|
||||
|
||||
export function updateInspector(session) {
|
||||
if (!inspectorEl || !session) return;
|
||||
|
||||
inspectorEl.innerHTML = '';
|
||||
|
||||
// Header
|
||||
inspectorEl.appendChild(buildSection('Session', `
|
||||
<div class="inspector-field">
|
||||
<span class="label">ID</span>
|
||||
<span class="value truncate" style="max-width:180px" title="${session.session_id}">${session.session_id.substring(0, 16)}...</span>
|
||||
</div>
|
||||
<div class="inspector-field">
|
||||
<span class="label">Status</span>
|
||||
<span class="badge ${session.status}">${session.status}</span>
|
||||
</div>
|
||||
<div class="inspector-field">
|
||||
<span class="label">Turns</span>
|
||||
<span class="value">${session.turn_count}</span>
|
||||
</div>
|
||||
<div class="inspector-field">
|
||||
<span class="label">Created</span>
|
||||
<span class="value">${formatTime(session.created_at)}</span>
|
||||
</div>
|
||||
<div class="inspector-field">
|
||||
<span class="label">Updated</span>
|
||||
<span class="value">${formatTime(session.updated_at)}</span>
|
||||
</div>
|
||||
`));
|
||||
|
||||
// Current Task
|
||||
if (session.current_task) {
|
||||
const task = session.current_task;
|
||||
let taskHtml = `
|
||||
<div class="inspector-field">
|
||||
<span class="label">Objective</span>
|
||||
</div>
|
||||
<div class="text-sm" style="margin-bottom:6px;color:var(--text-primary)">${escapeHtml(task.objective)}</div>
|
||||
<div class="inspector-field">
|
||||
<span class="label">Status</span>
|
||||
<span class="badge ${task.status}">${task.status}</span>
|
||||
</div>
|
||||
<div class="inspector-field">
|
||||
<span class="label">Step</span>
|
||||
<span class="value">${task.current_step_index + 1} / ${(task.plan || []).length}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Facts
|
||||
if (task.facts_extracted && task.facts_extracted.length > 0) {
|
||||
taskHtml += '<div class="mt-2"><span class="label text-sm">Facts:</span><ul style="padding-left:16px;margin-top:4px">';
|
||||
for (const f of task.facts_extracted.slice(-8)) {
|
||||
taskHtml += `<li class="text-sm">${escapeHtml(f)}</li>`;
|
||||
}
|
||||
taskHtml += '</ul></div>';
|
||||
}
|
||||
|
||||
// Constraints
|
||||
if (task.constraints && task.constraints.length > 0) {
|
||||
taskHtml += '<div class="mt-2"><span class="label text-sm">Constraints:</span><ul style="padding-left:16px;margin-top:4px">';
|
||||
for (const c of task.constraints) {
|
||||
taskHtml += `<li class="text-sm">${escapeHtml(c)}</li>`;
|
||||
}
|
||||
taskHtml += '</ul></div>';
|
||||
}
|
||||
|
||||
inspectorEl.appendChild(buildSection('Current Task', taskHtml));
|
||||
|
||||
// Plan
|
||||
if (task.plan && task.plan.length > 0) {
|
||||
let planHtml = '<div class="timeline">';
|
||||
for (let i = 0; i < task.plan.length; i++) {
|
||||
const step = task.plan[i];
|
||||
const stepStatus = step.status || 'pending';
|
||||
const isActive = i === task.current_step_index && task.status === 'executing';
|
||||
const stepClass = isActive ? 'active' : (stepStatus === 'completed' ? 'completed' : stepStatus === 'failed' ? 'failed' : '');
|
||||
|
||||
planHtml += `
|
||||
<div class="timeline-step ${stepClass}">
|
||||
<div class="timeline-step-header">
|
||||
<span class="role-badge ${step.agent_role || 'coder'}">${step.agent_role || 'coder'}</span>
|
||||
<span class="badge ${stepStatus}">${stepStatus}</span>
|
||||
</div>
|
||||
<div class="timeline-step-desc">${escapeHtml(step.description)}</div>
|
||||
${step.tools_used && step.tools_used.length > 0 ? `
|
||||
<div class="timeline-tools">
|
||||
${step.tools_used.map(t => `<span class="tool-chip">${escapeHtml(t)}</span>`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
${step.result_summary ? `<div class="text-sm text-muted mt-2">${escapeHtml(step.result_summary.substring(0, 200))}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
planHtml += '</div>';
|
||||
inspectorEl.appendChild(buildSection('Execution Plan', planHtml));
|
||||
}
|
||||
}
|
||||
|
||||
// Completed tasks
|
||||
if (session.completed_tasks && session.completed_tasks.length > 0) {
|
||||
let html = '<ul style="padding-left:16px">';
|
||||
for (const t of session.completed_tasks) {
|
||||
html += `<li class="text-sm mono">${t}</li>`;
|
||||
}
|
||||
html += '</ul>';
|
||||
inspectorEl.appendChild(buildSection(`Completed (${session.completed_tasks.length})`, html));
|
||||
}
|
||||
}
|
||||
|
||||
function buildSection(title, innerHtml) {
|
||||
const section = document.createElement('div');
|
||||
section.className = 'inspector-section';
|
||||
section.innerHTML = `<div class="inspector-section-title">${title}</div>${innerHtml}`;
|
||||
return section;
|
||||
}
|
||||
|
||||
function formatTime(isoStr) {
|
||||
if (!isoStr) return '—';
|
||||
try {
|
||||
return new Date(isoStr).toLocaleTimeString('en-US', { hour12: false });
|
||||
} catch {
|
||||
return isoStr;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
Reference in New Issue
Block a user