agent-ecosystem/src/renderer/components/terminal/TerminalLogPanel.tsx
iliya ec547e0662 refactor: enhance task management protocols and UI components
- Updated task management guidelines to emphasize the importance of posting task comments before marking tasks as complete, ensuring visibility of results on the task board.
- Introduced a new notification system for team leads after task completion, summarizing key findings and linking to detailed comments.
- Improved UI components, including a new CLI checking spinner with a delayed hint and enhanced task filters to support unread/read task states.
- Refactored sidebar task item styles to visually indicate unread tasks and adjusted task sorting options to include 'unread' as a criterion.
- Enhanced settings tabs with tooltips for better user guidance and improved layout for advanced settings options.
2026-03-20 13:45:03 +02:00

92 lines
2.3 KiB
TypeScript

import '@xterm/xterm/css/xterm.css';
import { useEffect, useRef } from 'react';
import { api } from '@renderer/api';
import { FitAddon } from '@xterm/addon-fit';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { Terminal } from '@xterm/xterm';
interface TerminalLogPanelProps {
/** Raw output chunks (with ANSI codes) to render */
chunks: string[];
/** CSS class for container */
className?: string;
}
export const TerminalLogPanel = ({
chunks,
className,
}: TerminalLogPanelProps): React.JSX.Element => {
const containerRef = useRef<HTMLDivElement>(null);
const termRef = useRef<Terminal | null>(null);
const writtenRef = useRef(0);
// Create xterm instance once
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const term = new Terminal({
cursorBlink: false,
disableStdin: true,
fontSize: 12,
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
scrollback: 200,
theme: {
background: '#141416',
foreground: '#fafafa',
cursor: 'transparent',
},
});
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
const webLinksAddon = new WebLinksAddon((_event, uri) => {
void api.openExternal(uri);
});
term.loadAddon(webLinksAddon);
term.open(container);
const rafId = requestAnimationFrame(() => fitAddon.fit());
const observer = new ResizeObserver(() => fitAddon.fit());
observer.observe(container);
termRef.current = term;
writtenRef.current = 0;
return () => {
cancelAnimationFrame(rafId);
observer.disconnect();
term.dispose();
termRef.current = null;
writtenRef.current = 0;
};
}, []);
// Write new chunks incrementally
useEffect(() => {
const term = termRef.current;
if (!term) return;
for (let i = writtenRef.current; i < chunks.length; i++) {
// xterm requires \r\n for proper line breaks; normalize bare \n from process output
term.write(chunks[i].replace(/\r?\n/g, '\r\n'));
}
writtenRef.current = chunks.length;
}, [chunks]);
return (
<div
ref={containerRef}
className={`mt-2 overflow-hidden rounded border ${className ?? ''}`}
style={{
borderColor: 'var(--color-border)',
height: '120px',
}}
/>
);
};