import { useState, type SyntheticEvent } from 'react' import { cn } from '@/lib/utils' import { TuiPane } from './chrome' import type { ResultRow, SearchRow, TransferRow, TransferStatus } from './data' const RESULT_COLS = '30px 3fr 2fr 74px 92px 78px 74px 42px' const TRANSFER_COLS = '48px 1fr 96px 168px 74px' interface SearchesPaneProps { searches: readonly SearchRow[] activeId: string active: boolean onPick: (id: string) => void onSubmit: (query: string) => void } export const SearchesPane = ({ searches, activeId, active, onPick, onSubmit, }: SearchesPaneProps) => { const [query, setQuery] = useState('') const submit = (event: SyntheticEvent) => { event.preventDefault() const trimmed = query.trim() if (trimmed === '') return onSubmit(trimmed) setQuery('') } return (
/ { setQuery(event.target.value) }} placeholder="search the network…" aria-label="Search the network" className="grow border-0 bg-transparent font-mono text-[13px] text-[var(--color-paper)] outline-none placeholder:text-[var(--color-dust)]" />
Status Query Results
{searches.map((search) => ( ))}
) } const resultGlyph = (row: ResultRow): { glyph: string; tone: string } => { if (row.queued === true) return { glyph: '✓', tone: 'text-[var(--color-phosphor)]' } if (row.marked === true) return { glyph: '[✓]', tone: 'text-[var(--color-oxide)]' } return { glyph: '[ ]', tone: 'text-[var(--color-dust)]' } } interface ResultsPaneProps { query: string rows: readonly ResultRow[] selected: number active: boolean onSelect: (index: number) => void onQueue: (index: number) => void } export const ResultsPane = ({ query, rows, selected, active, onSelect, onQueue, }: ResultsPaneProps) => (
✓ Filename Folder Size User Bitrate Speed Slots
{rows.map((row, i) => { const { glyph, tone } = resultGlyph(row) const parts = row.name.split('\\') const isSelected = selected === i return ( ) })}
) const TRANSFER_GLYPH: Record = { queued: ['⋯', 'text-[var(--color-tape)]'], done: ['✓', 'text-[var(--color-phosphor)]'], active: ['↯', 'text-[var(--color-oxide)]'], fail: ['✗', 'text-[var(--color-alarm)]'], } const PROGRESS_TONE: Record = { queued: 'text-[var(--color-paper)]', active: 'text-[var(--color-paper)]', done: 'text-[var(--color-phosphor)]', fail: 'text-[var(--color-alarm)]', } interface TransfersPaneProps { rows: readonly TransferRow[] active: boolean } export const TransfersPane = ({ rows, active }: TransfersPaneProps) => (
Status Filename User Progress Speed
{rows.map((row) => { const [glyph, tone] = TRANSFER_GLYPH[row.status] return (
{glyph} {row.name} {row.user} {row.progress} {row.speed}
) })}
) interface InfoPaneProps { row: ResultRow | undefined } export const InfoPane = ({ row }: InfoPaneProps) => { const parts = (row?.name ?? '').split('\\') const file = parts.at(-1) ?? '—' const path = parts.slice(0, -1).join('/') const queued = row?.queued === true const fields: readonly (readonly [string, string, string])[] = [ ['User', row?.user ?? '—', 'text-[var(--color-signal)]'], ['Size', row?.size ?? '—', 'text-[var(--color-tape)]'], [ 'Status', queued ? 'Queued' : 'Available', queued ? 'text-[var(--color-tape)]' : 'text-[var(--color-phosphor)]', ], ['Bitrate', row?.bitrate ?? '-', 'text-[var(--color-paper)]'], ] return (
{file}
{path === '' ? '—' : path}
{fields.map(([key, value, tone]) => (
{key} {value}
))} Queue position {queued ? '3' : 'unknown'} Free slots {row !== undefined && row.slots > 0 ? `${String(row.slots)} available` : '0'}
) }