@alavida/agentpack
Version:
Compiler-driven lifecycle CLI for source-backed agent skills
219 lines (208 loc) • 6.86 kB
JSX
const STATUS_COLORS = {
current: 'var(--status-current)',
stale: 'var(--status-stale)',
affected: 'var(--status-affected)',
changed: 'var(--status-stale)',
unknown: 'var(--status-unknown)',
};
function StatusPill({ status }) {
const color = STATUS_COLORS[status] || STATUS_COLORS.unknown;
return (
<span style={{
fontFamily: 'var(--font-mono)',
fontSize: 10,
padding: '3px 10px',
letterSpacing: '0.04em',
background: `color-mix(in srgb, ${color} 12%, transparent)`,
color,
textTransform: 'uppercase',
}}>
{status}
</span>
);
}
function MetaRow({ label, children }) {
return (
<div style={{ marginTop: 16 }}>
<div style={{
fontFamily: 'var(--font-mono)',
fontVariant: 'small-caps',
textTransform: 'uppercase',
letterSpacing: 3,
fontSize: 9,
color: 'var(--text-faint)',
marginBottom: 6,
}}>
{label}
</div>
<div style={{ fontSize: 14, color: 'var(--text-dim)', lineHeight: 1.6 }}>
{children}
</div>
</div>
);
}
export function InspectorPanel({ node, onClose, onNavigate }) {
return (
<aside
data-testid="inspector-panel"
style={{
position: 'fixed',
top: 0,
right: 0,
width: node ? 340 : 0,
height: '100vh',
background: 'var(--surface)',
borderLeft: '1px solid var(--border)',
transition: 'width 300ms ease',
overflow: 'hidden',
zIndex: 20,
display: 'flex',
flexDirection: 'column',
}}
>
{node && (
<div style={{ padding: '28px 24px', overflowY: 'auto', flex: 1 }}>
{/* Close button */}
<button
data-testid="inspector-close"
type="button"
onClick={onClose}
style={{
position: 'absolute',
top: 16,
right: 16,
background: 'none',
border: 'none',
color: 'var(--text-dim)',
cursor: 'pointer',
fontFamily: 'var(--font-mono)',
fontSize: 16,
padding: 4,
lineHeight: 1,
transition: 'color 200ms ease',
}}
onMouseEnter={(e) => { e.target.style.color = 'var(--text)'; }}
onMouseLeave={(e) => { e.target.style.color = 'var(--text-dim)'; }}
>
×
</button>
{/* Type label */}
<div style={{
fontFamily: 'var(--font-mono)',
fontVariant: 'small-caps',
textTransform: 'uppercase',
letterSpacing: 3,
fontSize: 9,
color: 'var(--text-dim)',
marginBottom: 6,
}}>
{node.type}
</div>
{/* Name */}
<h2 style={{
fontFamily: 'var(--font-body)',
fontSize: 24,
fontWeight: 400,
fontStyle: 'italic',
color: 'var(--text)',
margin: '0 0 12px 0',
lineHeight: 1.3,
paddingRight: 24,
}}>
{node.name || node.path?.split('/').slice(-1)[0] || node.id}
</h2>
{/* Status */}
<StatusPill status={node.status} />
{/* Description */}
{node.description && (
<MetaRow label="Description">
{node.description}
</MetaRow>
)}
{/* Source-specific fields */}
{node.type === 'source' && (
<>
<MetaRow label="Path">
<span style={{ fontFamily: 'var(--font-mono)', fontSize: 12 }}>
{node.path}
</span>
</MetaRow>
{node.usedBy && node.usedBy.length > 0 && (
<MetaRow label="Used by">
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
{node.usedBy.map((skillName) => (
<span key={skillName} style={{
fontFamily: 'var(--font-mono)',
fontSize: 11,
color: 'var(--edge-provenance)',
}}>
{skillName}
</span>
))}
</div>
</MetaRow>
)}
</>
)}
{/* Skill/Dependency fields */}
{(node.type === 'skill' || node.type === 'dependency' || node.type === 'internal-skill' || node.type === 'external-package') && (
<>
{node.version && (
<MetaRow label="Version">
<span style={{ fontFamily: 'var(--font-mono)', fontSize: 12 }}>
{node.version}
</span>
</MetaRow>
)}
{(node.type === 'internal-skill' || node.type === 'external-package') && (
<MetaRow label="Dependency Type">
{node.type === 'internal-skill' ? 'Internal sub-skill' : 'External package'}
</MetaRow>
)}
{node.type === 'skill' && (
<MetaRow label="Source Material">
{node.sourceSummary || 'No bound source material in this graph'}
</MetaRow>
)}
<MetaRow label="Explanation">
{node.explanation}
</MetaRow>
</>
)}
{/* Navigate into dependency */}
{(node.type === 'dependency' || node.type === 'external-package' || node.type === 'internal-skill') && onNavigate && (
<button
data-testid="inspector-navigate"
type="button"
onClick={() => onNavigate(node.navigationTarget || node.packageName)}
style={{
marginTop: 24,
width: '100%',
fontFamily: 'var(--font-mono)',
fontSize: 11,
background: 'transparent',
border: '1px solid var(--border-bright)',
color: 'var(--text-dim)',
padding: '10px 16px',
cursor: 'pointer',
transition: 'all 200ms ease',
letterSpacing: '0.04em',
textAlign: 'left',
}}
onMouseEnter={(e) => {
e.target.style.color = 'var(--text)';
e.target.style.borderColor = 'var(--status-current)';
}}
onMouseLeave={(e) => {
e.target.style.color = 'var(--text-dim)';
e.target.style.borderColor = 'var(--border-bright)';
}}
>
View skill graph →
</button>
)}
</div>
)}
</aside>
);
}