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;