import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import { matchesKey } from "@earendil-works/pi-tui"; import { fmt } from "../format"; import type { ListRow } from "../types"; import { showPanel } from "./panel"; /** Scrollable list panel (↑↓/PgUp/PgDn/Home/End), closed with Esc/Enter. No-op outside TUI mode. */ export const showListUi = async ( ctx: ExtensionCommandContext, title: string, rows: ListRow[], ): Promise => { if (ctx.mode !== "tui") return; const sorted = [...rows].sort((a, b) => b.tokens - a.tokens); if (sorted.length === 0) { await showPanel(ctx, ctx.ui.theme.fg("dim", `No ${title.toLowerCase()} loaded.`)); return; } await ctx.ui.custom((tui, theme, _kb, done) => { let offset = 0; const pageHeight = () => Math.max(8, tui.terminal.rows - 6); const render = (width: number): string[] => { const nameCol = 26; const tokCol = 8; const descCol = Math.max(8, width - nameCol - tokCol - 6); const page = pageHeight(); const out: string[] = []; out.push(theme.fg("accent", theme.bold(title))); out.push( theme.fg( "dim", `${sorted.length} items, largest first — ↑↓/PgUp/PgDn scroll · Esc/Enter close`, ), ); out.push(""); for (let i = offset; i < Math.min(offset + page, sorted.length); i++) { const r = sorted[i]; const name = r.name.length > nameCol - 1 ? r.name.slice(0, nameCol - 2) + "…" : r.name; const desc = r.desc.length > descCol ? r.desc.slice(0, descCol - 1) + "…" : r.desc; const highlight = i === offset ? "accent" : "text"; out.push( theme.fg( highlight, name.padEnd(nameCol) + desc.padEnd(descCol) + " " + fmt(r.tokens), ), ); } out.push(""); out.push( theme.fg( "dim", `Showing ${offset + 1}–${Math.min(offset + page, sorted.length)} of ${sorted.length}`, ), ); return out; }; return { render, invalidate: () => {}, handleInput: (data: string) => { const page = pageHeight(); if (matchesKey(data, "up")) offset = Math.max(0, offset - 1); else if (matchesKey(data, "down")) offset = Math.min(sorted.length - 1, offset + 1); else if (matchesKey(data, "pageUp")) offset = Math.max(0, offset - page); else if (matchesKey(data, "pageDown")) offset = Math.min(sorted.length - 1, offset + page); else if (matchesKey(data, "home")) offset = 0; else if (matchesKey(data, "end")) offset = Math.max(0, sorted.length - 1); else if (matchesKey(data, "enter") || matchesKey(data, "escape")) done(undefined); }, }; }); };