import { type ReactNode, useEffect, useState } from 'react'; import { Box, Text } from '@anthropic/ink'; import type { SandboxViolationEvent } from '../utils/sandbox/sandbox-adapter.js'; import { SandboxManager } from '../utils/sandbox/sandbox-adapter.js'; /** * Format a timestamp as "h:mm:ssa" (e.g., "1:30:45pm"). * Replaces date-fns format() to avoid pulling in a 39MB dependency for one call. */ function formatTime(date: Date): string { const h = date.getHours() % 12 || 12; const m = String(date.getMinutes()).padStart(2, '0'); const s = String(date.getSeconds()).padStart(2, '0'); const ampm = date.getHours() < 12 ? 'am' : 'pm'; return `${h}:${m}:${s}${ampm}`; } import { getPlatform } from 'src/utils/platform.js'; export function SandboxViolationExpandedView(): ReactNode { const [violations, setViolations] = useState([]); const [totalCount, setTotalCount] = useState(0); useEffect(() => { // This is harmless if sandboxing is not enabled const store = SandboxManager.getSandboxViolationStore(); const unsubscribe = store.subscribe((allViolations: SandboxViolationEvent[]) => { setViolations(allViolations.slice(-10)); setTotalCount(store.getTotalCount()); }); return unsubscribe; }, []); if (!SandboxManager.isSandboxingEnabled() || getPlatform() === 'linux') { return null; } if (totalCount === 0) { return null; } return ( ⧈ Sandbox blocked {totalCount} total {totalCount === 1 ? 'operation' : 'operations'} {violations.map((v, i) => ( {formatTime(v.timestamp)} {v.command ? ` ${v.command}:` : ''} {v.line} ))} … showing last {Math.min(10, violations.length)} of {totalCount} ); }