From 97bf2568a462d4fd647c6ad5a2a83ab25a582364 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Mon, 27 Apr 2026 13:25:47 -0500 Subject: [PATCH] refactor(tui): make learning ledger master-detail Keep recent learning entries in a left-hand list and show the selected item details in a right-side pane only when expanded. --- ui-tui/src/components/learningLedger.tsx | 88 ++++++++++++------------ 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/ui-tui/src/components/learningLedger.tsx b/ui-tui/src/components/learningLedger.tsx index 352e3f1dce..f6aaff029c 100644 --- a/ui-tui/src/components/learningLedger.tsx +++ b/ui-tui/src/components/learningLedger.tsx @@ -10,7 +10,7 @@ import { OverlayHint, windowItems, windowOffset } from './overlayControls.js' const EDGE_GUTTER = 10 const MAX_WIDTH = 132 const MIN_WIDTH = 64 -const VISIBLE_ROWS = 10 +const VISIBLE_ROWS = 12 const typeIcon: Record = { integration: '◇', @@ -46,9 +46,6 @@ export function LearningLedger({ gw, onClose, t }: LearningLedgerProps) { const [loading, setLoading] = useState(true) const { stdout } = useStdout() const width = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, (stdout?.columns ?? 80) - EDGE_GUTTER)) - const columns = width >= 92 ? 2 : 1 - const pageSize = VISIBLE_ROWS * columns - const colWidth = columns === 2 ? Math.floor((width - 3) / 2) : width useEffect(() => { gw.request('learning.ledger', { limit: 120 }) @@ -62,6 +59,9 @@ export function LearningLedger({ gw, onClose, t }: LearningLedgerProps) { const items = ledger?.items ?? [] const selected = items[idx] + const detailOpen = expanded && !!selected + const listWidth = detailOpen ? Math.max(38, Math.floor(width * 0.48)) : width + const detailWidth = Math.max(28, width - listWidth - 3) const counts = useMemo( () => Object.entries(ledger?.counts ?? {}) @@ -78,26 +78,14 @@ export function LearningLedger({ gw, onClose, t }: LearningLedgerProps) { return } - if (key.leftArrow && columns === 2 && idx > 0) { + if (key.upArrow && idx > 0) { setIdx(v => v - 1) return } - if (key.rightArrow && columns === 2 && idx < items.length - 1) { - setIdx(v => v + 1) - - return - } - - if (key.upArrow && idx > 0) { - setIdx(v => Math.max(0, v - columns)) - - return - } - if (key.downArrow && idx < items.length - 1) { - setIdx(v => Math.min(items.length - 1, v + columns)) + setIdx(v => v + 1) return } @@ -110,7 +98,7 @@ export function LearningLedger({ gw, onClose, t }: LearningLedgerProps) { const n = ch === '0' ? 10 : parseInt(ch, 10) if (!Number.isNaN(n) && n >= 1 && n <= Math.min(10, items.length)) { - const next = windowOffset(items.length, idx, pageSize) + n - 1 + const next = windowOffset(items.length, idx, VISIBLE_ROWS) + n - 1 if (items[next]) { setIdx(next) @@ -146,10 +134,7 @@ export function LearningLedger({ gw, onClose, t }: LearningLedgerProps) { ) } - const { items: visible, offset } = windowItems(items, idx, pageSize) - const rows = Array.from({ length: Math.ceil(visible.length / columns) }, (_, row) => - visible.slice(row * columns, row * columns + columns) - ) + const { items: visible, offset } = windowItems(items, idx, VISIBLE_ROWS) return ( @@ -164,43 +149,32 @@ export function LearningLedger({ gw, onClose, t }: LearningLedgerProps) { ) : null} {offset > 0 && ↑ {offset} more} - {rows.map((row, rowIdx) => ( - - {row.map((item, colIdx) => { - const visibleIdx = rowIdx * columns + colIdx - const absolute = offset + visibleIdx + + + {visible.map((item, i) => { + const absolute = offset + i const active = absolute === idx return ( ) })} - ))} - {offset + pageSize < items.length && ↓ {items.length - offset - pageSize} more} + {detailOpen && selected ? : null} + - {selected && expanded ? ( - - - {selected.type === 'memory' || selected.type === 'user' ? selected.name : selected.summary} - - {selected.type === 'memory' || selected.type === 'user' ? ( - {selected.summary} - ) : null} - source: {selected.source} - - ) : null} + {offset + VISIBLE_ROWS < items.length && ↓ {items.length - offset - VISIBLE_ROWS} more} - {`${columns === 2 ? '↑↓←→ select' : '↑/↓ select'} · Enter/Space details · 1-9,0 quick · Esc/q close`} + ↑/↓ select · Enter/Space details · 1-9,0 quick · Esc/q close ) @@ -228,6 +202,24 @@ function LedgerRow({ active, index, item, t, width }: LedgerRowProps) { ) } +function LedgerDetails({ item, t, width }: LedgerDetailsProps) { + const memoryLike = item.type === 'memory' || item.type === 'user' + + return ( + + + Details + + + {memoryLike ? item.name : item.summary} + + {memoryLike ? {item.summary} : null} + {item.count ? used: {item.count}× : null} + source: {item.source} + + ) +} + interface LearningLedgerItem { count?: number last_used_at?: null | number @@ -255,6 +247,12 @@ interface LedgerRowProps { width: number } +interface LedgerDetailsProps { + item: LearningLedgerItem + t: Theme + width: number +} + interface LearningLedgerProps { gw: GatewayClient onClose: () => void