Skip to content

Commit 00bc8e6

Browse files
committed
fix UI
1 parent 49863fb commit 00bc8e6

4 files changed

Lines changed: 42 additions & 4 deletions

File tree

frontend/src/pages/Library.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ describe('Library page', () => {
3838
await waitFor(() => expect(screen.getByText(/golang-testing/i)).toBeInTheDocument())
3939
})
4040

41+
it('keeps rendering when search returns a null items payload', async () => {
42+
vi.spyOn(api, 'searchMetadata')
43+
.mockResolvedValueOnce({ items: [metadataItem({ name: 'initial-skill', kind: 'skill', install_key: 'o/r:initial-skill' })], total: 1, limit: 20, offset: 0 })
44+
.mockResolvedValueOnce({ items: null as unknown as MetadataItem[], total: 0, limit: 20, offset: 0 })
45+
render(<Library kind="skill" />)
46+
47+
expect(await screen.findByText(/initial-skill/i)).toBeInTheDocument()
48+
fireEvent.change(screen.getByLabelText(/skills search/i), { target: { value: 'zzzz-no-match' } })
49+
fireEvent.click(screen.getByRole('button', { name: /search/i }))
50+
51+
await waitFor(() => expect(screen.getByText(/no skills found/i)).toBeInTheDocument())
52+
expect(screen.getByRole('heading', { name: /skills/i })).toBeInTheDocument()
53+
vi.restoreAllMocks()
54+
})
55+
4156
it('renders subagents page', async () => {
4257
render(<Library kind="agent" />)
4358
expect(await screen.findByRole('heading', { name: /subagents/i })).toBeInTheDocument()

frontend/src/pages/Library.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ export function Library({ kind }: LibraryProps) {
4949
setLoading(true)
5050
try {
5151
const resp = await api.searchMetadata(kind, q, PAGE_SIZE, off)
52-
setItems(resp.items)
53-
setTotal(resp.total)
52+
setItems(resp.items ?? [])
53+
setTotal(resp.total ?? 0)
5454
return resp
5555
} catch (err) {
5656
setStatus(`Search failed: ${err instanceof Error ? err.message : String(err)}`)
@@ -69,8 +69,9 @@ export function Library({ kind }: LibraryProps) {
6969
// self-heal under a purely empty check, because the kind isn't empty.
7070
const loadOrAutoRefresh = useCallback(async (q: string, off: number) => {
7171
const resp = await load(q, off)
72+
const respItems = resp?.items ?? []
7273
const isEmpty = !resp || resp.total === 0
73-
const isStale = !!resp && resp.items.length > 0 && resp.items.some((item) => !item.install_key.includes(':'))
74+
const isStale = respItems.length > 0 && respItems.some((item) => !item.install_key.includes(':'))
7475
if ((isEmpty || isStale) && q === '' && off === 0 && !autoRefreshed.current) {
7576
autoRefreshed.current = true
7677
setRefreshing(true)

internal/metadata/store.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func (s *Store) MarkInstalled(ctx context.Context, kind, installKey, targetApp s
271271
}
272272

273273
func scanItems(rows *sql.Rows) ([]Item, error) {
274-
var items []Item
274+
items := []Item{}
275275
for rows.Next() {
276276
var it Item
277277
var installed int

internal/metadata/store_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,28 @@ func TestSearchByKindFilter(t *testing.T) {
7070
}
7171
}
7272

73+
func TestSearchNoMatchesReturnsEmptySlice(t *testing.T) {
74+
ctx := context.Background()
75+
s := NewStore(filepath.Join(t.TempDir(), "cam.db"))
76+
if err := s.Init(ctx); err != nil {
77+
t.Fatal(err)
78+
}
79+
if err := s.UpsertItem(ctx, Item{Kind: "skill", Name: "present", InstallKey: "a/b:present"}); err != nil {
80+
t.Fatalf("UpsertItem: %v", err)
81+
}
82+
83+
results, err := s.Search(ctx, SearchQuery{Query: "missing", Kind: "skill"})
84+
if err != nil {
85+
t.Fatalf("Search: %v", err)
86+
}
87+
if results == nil {
88+
t.Fatal("expected non-nil empty slice for no matches")
89+
}
90+
if len(results) != 0 {
91+
t.Fatalf("expected 0 results, got %d", len(results))
92+
}
93+
}
94+
7395
func TestSearchPagination(t *testing.T) {
7496
ctx := context.Background()
7597
s := NewStore(filepath.Join(t.TempDir(), "cam.db"))

0 commit comments

Comments
 (0)