import React from 'react'; import { Box, Text } from 'ink'; import type { Peer } from '../../engine/types.js'; import { colors, borders } from '../theme/index.js'; import { formatSpeedCompact, formatProgress, formatAddress, countryCodeToFlag, } from '../utils/format.js'; export interface PeerListProps { /** Array of connected peers to display */ peers: Peer[]; } /** * Column widths for peer list display */ const COLUMN_WIDTHS = { flag: 3, address: 22, client: 16, progress: 6, download: 9, upload: 9, flags: 6, } as const; /** * Format peer flags into a readable string * * Flags: * - D: Downloading from peer * - U: Uploading to peer * - d: Peer wants to download from us * - u: Peer wants to upload to us * - K: We are choking peer * - k: Peer is choking us */ function formatPeerFlags(flags: Peer['flags']): string { const result: string[] = []; // Choking states if (flags.amChoking) result.push('K'); if (flags.peerChoking) result.push('k'); // Interest states if (flags.amInterested) result.push('d'); if (flags.peerInterested) result.push('u'); return result.join('') || '-'; } /** * Truncate client name to fit column */ function truncateClient(client: string, maxLength: number): string { if (client.length <= maxLength) { return client.padEnd(maxLength); } return client.slice(0, maxLength - 1) + '\u2026'; } /** * Header row for peer list */ const PeerListHeader: React.FC = () => { return ( {' '} Address Client Prog {'\u2193'} Down {'\u2191'} Up Flags {borders.horizontal.repeat( COLUMN_WIDTHS.flag + COLUMN_WIDTHS.address + COLUMN_WIDTHS.client + COLUMN_WIDTHS.progress + COLUMN_WIDTHS.download + COLUMN_WIDTHS.upload + COLUMN_WIDTHS.flags )} ); }; /** * Single peer row component */ const PeerRow: React.FC<{ peer: Peer }> = ({ peer }) => { const flag = countryCodeToFlag(peer.country); const address = formatAddress(peer.ip, peer.port); const client = truncateClient(peer.client || 'Unknown', COLUMN_WIDTHS.client); const progress = formatProgress(peer.progress); const downloadSpeed = formatSpeedCompact(peer.downloadSpeed); const uploadSpeed = formatSpeedCompact(peer.uploadSpeed); const flags = formatPeerFlags(peer.flags); // Color speeds based on activity const downloadColor = peer.downloadSpeed > 0 ? colors.success : colors.muted; const uploadColor = peer.uploadSpeed > 0 ? colors.primary : colors.muted; return ( {flag} {address.padEnd(COLUMN_WIDTHS.address)} {client} {progress.padStart(COLUMN_WIDTHS.progress)} {downloadSpeed.padStart(COLUMN_WIDTHS.download)} {uploadSpeed.padStart(COLUMN_WIDTHS.upload)} {flags} ); }; /** * Empty state when no peers are connected */ const EmptyState: React.FC = () => { return ( No peers connected ); }; /** * PeerList component for displaying connected peers * * Shows a table of connected peers with their IP:Port, client name, * download progress, transfer speeds, and connection flags. * * @example * * * // Output: * // Address Client Prog Down Up Flags * // ───────────────────────────────────────────────────────────────────── * // 192.168.1.100:6881 qBittorrent 4.5 100% 0B/s 256K/s Ku * // 10.0.0.50:51413 Transmission 3 45% 1.2M/s 0B/s kd */ export const PeerList: React.FC = ({ peers }) => { if (peers.length === 0) { return ; } return ( {peers.map((peer) => ( ))} ); }; export default PeerList;