From 00edeb478b246936d415b2a3a9f2d98b69ea342a Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sun, 25 Oct 2026 20:25:00 +1100 Subject: [PATCH] Show virtual parallel_fetch in UI and TUI (#173) Virtuals carry a `parallel_fetch` flag in the v2 API, but the UI type omits it and neither the UI nor the TUI displays it, so the setting is invisible to operators. - add `parallel_fetch` to the TS `Virtual` type - add a Parallel column with a yellow badge to the UI virtuals table - use one `selected` lookup for the virtual detail panel - add a parallel column to the TUI virtuals list, padding the member count (`%3d members`) so columns align Reviewed-on: https://git.unkin.net/unkin/artifactapi/pulls/173 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- internal/tui/render.go | 8 +++++-- ui/src/api/types.ts | 1 + ui/src/pages/Virtuals.tsx | 44 ++++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/internal/tui/render.go b/internal/tui/render.go index c18b260..d943995 100644 --- a/internal/tui/render.go +++ b/internal/tui/render.go @@ -109,8 +109,12 @@ func (m *model) viewVirtualsList() string { } for i, v := range m.virtuals { - line := fmt.Sprintf(" %-25s %-12s %d members %s", - v.Name, v.PackageType, len(v.Members), v.Description) + parallel := "-" + if v.ParallelFetch { + parallel = "parallel" + } + line := fmt.Sprintf(" %-25s %-12s %3d members %-8s %s", + v.Name, v.PackageType, len(v.Members), parallel, v.Description) if i == m.cursor { sb.WriteString(selStyle.Render(line)) } else { diff --git a/ui/src/api/types.ts b/ui/src/api/types.ts index bc6b020..0ef63e7 100644 --- a/ui/src/api/types.ts +++ b/ui/src/api/types.ts @@ -32,6 +32,7 @@ export interface Virtual { description: string; members: string[]; managed_by: string; + parallel_fetch: boolean; created_at: string; updated_at: string; } diff --git a/ui/src/pages/Virtuals.tsx b/ui/src/pages/Virtuals.tsx index 41dceb1..0920db8 100644 --- a/ui/src/pages/Virtuals.tsx +++ b/ui/src/pages/Virtuals.tsx @@ -26,6 +26,8 @@ export function Virtuals() { .finally(() => setLoading(false)); }, []); + const selected = virtuals.find(x => x.name === expanded); + function memberLink(name: string) { const remote = remoteMap[name]; if (remote?.repo_type === 'local') { @@ -67,6 +69,13 @@ export function Virtuals() { header: 'Description', render: (v: Virtual) => v.description || —, }, + { + key: 'parallel', + header: 'Parallel', + render: (v: Virtual) => + v.parallel_fetch ? parallel : —, + width: '100px', + }, { key: 'managed', header: 'Managed', @@ -81,30 +90,23 @@ export function Virtuals() { /> )} - {expanded && ( + {selected && (
-

Members of {expanded}

+

Members of {selected.name}

    - {virtuals - .find(v => v.name === expanded) - ?.members?.map((m, i) => { - const remote = remoteMap[m]; - const typeLabel = remote?.repo_type === 'local' ? 'local' : 'remote'; - return ( -
  • - {i + 1} - {m} - {typeLabel} -
  • - ); - })} + {selected.members?.map((m, i) => { + const remote = remoteMap[m]; + const typeLabel = remote?.repo_type === 'local' ? 'local' : 'remote'; + return ( +
  • + {i + 1} + {m} + {typeLabel} +
  • + ); + })}
- {(() => { - const v = virtuals.find(x => x.name === expanded); - return v ? ( - - ) : null; - })()} +
)}