Skip to content

Commit d20278e

Browse files
samuvCopilot
andauthored
feat(skills): polish install/remove actions and detail header spacing (#2128)
* fix(skills): hide local builds toolbar when there are no builds * fix(skills): widen metadata badge gap on detail header to 16px * feat(skills): polish build detail actions (round Remove, reorder, install icon) * feat(skills): reorder build actions and add Install icon in card and table * feat(skills): add Install icon across registry card, table, and detail * fix(skills): use non-suspense query for builds toolbar visibility Avoid suspending the whole Skills page (Registry/Installed tabs) on the builds endpoint. Addresses Copilot review on #2128. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 308c106 commit d20278e

9 files changed

Lines changed: 71 additions & 36 deletions

File tree

renderer/src/features/registry-servers/components/registry-detail-header.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Button } from '@/common/components/ui/button'
22
import { LinkViewTransition } from '@/common/components/link-view-transition'
3+
import { cn } from '@/common/lib/utils'
34
import { ChevronLeft } from 'lucide-react'
45
import type { MouseEvent, ReactNode } from 'react'
56
import { useCanGoBack, useRouter } from '@tanstack/react-router'
@@ -9,6 +10,7 @@ type RegistryDetailHeaderProps = {
910
backTo?: string
1011
backSearch?: Record<string, unknown>
1112
badges?: ReactNode
13+
badgesClassName?: string
1214
description?: string | null
1315
/**
1416
* When true, clicking the back button navigates using browser history
@@ -24,6 +26,7 @@ export function RegistryDetailHeader({
2426
backTo = '/registry',
2527
backSearch,
2628
badges,
29+
badgesClassName,
2730
description,
2831
historyBack = false,
2932
}: RegistryDetailHeaderProps) {
@@ -63,7 +66,11 @@ export function RegistryDetailHeader({
6366
</div>
6467
<div className="flex flex-col gap-2">
6568
<h1 className="text-page-title m-0 p-0">{title}</h1>
66-
{badges && <div className="flex items-center gap-2">{badges}</div>}
69+
{badges && (
70+
<div className={cn('flex items-center gap-2', badgesClassName)}>
71+
{badges}
72+
</div>
73+
)}
6774
{description ? (
6875
<div className="text-muted-foreground mt-5 flex-2 select-none">
6976
{description}

renderer/src/features/skills/components/build-detail-page.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import {
66
TooltipContent,
77
TooltipTrigger,
88
} from '@/common/components/ui/tooltip'
9-
import { TagIcon, CodeIcon, FingerprintIcon, Trash2Icon } from 'lucide-react'
9+
import {
10+
TagIcon,
11+
CodeIcon,
12+
FingerprintIcon,
13+
PlusIcon,
14+
Trash2Icon,
15+
} from 'lucide-react'
1016
import type { GithubComStacklokToolhivePkgSkillsLocalBuild as LocalBuild } from '@common/api/generated/types.gen'
1117
import { DialogInstallSkill } from './dialog-install-skill'
1218
import { DialogDeleteBuild } from './dialog-delete-build'
@@ -90,27 +96,29 @@ export function BuildDetailPage({ build }: BuildDetailPageProps) {
9096
actions={
9197
<div className="flex items-center gap-3">
9298
<Button
93-
variant="secondary"
99+
variant="action"
94100
onClick={() => {
95-
trackEvent('Skills: delete build dialog opened', {
101+
trackEvent('Skills: install dialog opened', {
96102
source: 'build_detail',
97103
})
98-
setDeleteOpen(true)
104+
setInstallOpen(true)
99105
}}
100106
>
101-
<Trash2Icon className="size-4" />
102-
Remove
107+
<PlusIcon className="size-4" />
108+
Install
103109
</Button>
104110
<Button
105-
variant="action"
111+
variant="secondary"
112+
className="rounded-full"
106113
onClick={() => {
107-
trackEvent('Skills: install dialog opened', {
114+
trackEvent('Skills: delete build dialog opened', {
108115
source: 'build_detail',
109116
})
110-
setInstallOpen(true)
117+
setDeleteOpen(true)
111118
}}
112119
>
113-
Install
120+
<Trash2Icon className="size-4" />
121+
Remove
114122
</Button>
115123
</div>
116124
}

renderer/src/features/skills/components/card-build.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from 'react'
22
import { Button } from '@/common/components/ui/button'
33
import { Badge } from '@/common/components/ui/badge'
4-
import { Trash2Icon } from 'lucide-react'
4+
import { PlusIcon, Trash2Icon } from 'lucide-react'
55
import { useNavigate } from '@tanstack/react-router'
66
import type { GithubComStacklokToolhivePkgSkillsLocalBuild as LocalBuild } from '@common/api/generated/types.gen'
77
import { DialogInstallSkill } from './dialog-install-skill'
@@ -68,29 +68,30 @@ export function CardBuild({ build }: { build: LocalBuild }) {
6868
className="relative z-10 rounded-full"
6969
onClick={(e) => {
7070
e.stopPropagation()
71-
trackEvent('Skills: delete build dialog opened', {
71+
trackEvent('Skills: install dialog opened', {
7272
source: 'build_card',
7373
})
74-
setDeleteOpen(true)
74+
setInstallOpen(true)
7575
}}
76-
aria-label={`Remove ${title}`}
76+
aria-label={`Install ${title}`}
7777
>
78-
<Trash2Icon className="size-4" />
79-
Remove
78+
<PlusIcon className="size-4" />
79+
Install
8080
</Button>
8181
<Button
8282
variant="secondary"
8383
className="relative z-10 rounded-full"
8484
onClick={(e) => {
8585
e.stopPropagation()
86-
trackEvent('Skills: install dialog opened', {
86+
trackEvent('Skills: delete build dialog opened', {
8787
source: 'build_card',
8888
})
89-
setInstallOpen(true)
89+
setDeleteOpen(true)
9090
}}
91-
aria-label={`Install ${title}`}
91+
aria-label={`Remove ${title}`}
9292
>
93-
Install
93+
<Trash2Icon className="size-4" />
94+
Remove
9495
</Button>
9596
</>
9697
}

renderer/src/features/skills/components/card-registry-skill.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from 'react'
2-
import { Github } from 'lucide-react'
2+
import { Github, PlusIcon } from 'lucide-react'
33
import { Button } from '@/common/components/ui/button'
44
import { useNavigate } from '@tanstack/react-router'
55
import type { RegistrySkill } from '@common/api/generated/types.gen'
@@ -80,6 +80,7 @@ export function CardRegistrySkill({ skill }: { skill: RegistrySkill }) {
8080
setInstallOpen(true)
8181
}}
8282
>
83+
<PlusIcon className="size-4" />
8384
Install
8485
</Button>
8586
</>

renderer/src/features/skills/components/skill-detail-layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export function SkillDetailLayout({
8282
backTo={backTo}
8383
backSearch={backSearch}
8484
badges={badges}
85+
badgesClassName="gap-4"
8586
historyBack={historyBack}
8687
/>
8788
</div>
@@ -125,7 +126,7 @@ export function SkillDetailLayout({
125126
{title}
126127
</h2>
127128
{badges && (
128-
<div className="flex flex-wrap items-center gap-2">
129+
<div className="flex flex-wrap items-center gap-4">
129130
{badges}
130131
</div>
131132
)}

renderer/src/features/skills/components/skill-detail-page.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { useState } from 'react'
22
import { Button } from '@/common/components/ui/button'
3-
import { TagIcon, GitForkIcon, GithubIcon, ScaleIcon } from 'lucide-react'
3+
import {
4+
TagIcon,
5+
GitForkIcon,
6+
GithubIcon,
7+
PlusIcon,
8+
ScaleIcon,
9+
} from 'lucide-react'
410
import type { RegistrySkill } from '@common/api/generated/types.gen'
511
import { DialogInstallSkill } from './dialog-install-skill'
612
import { SkillDetailLayout } from './skill-detail-layout'
@@ -80,6 +86,7 @@ export function SkillDetailPage({ skill }: SkillDetailPageProps) {
8086
setInstallOpen(true)
8187
}}
8288
>
89+
<PlusIcon className="size-4" />
8390
Install
8491
</Button>
8592
{skill.repository?.url && (

renderer/src/features/skills/components/skills-page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState } from 'react'
22
import { useSuspenseQuery, useQuery } from '@tanstack/react-query'
33
import {
4+
getApiV1BetaSkillsBuildsOptions,
45
getApiV1BetaSkillsOptions,
56
getRegistryByRegistryNameV01xDevToolhiveSkillsOptions,
67
} from '@common/api/generated/@tanstack/react-query.gen'
@@ -158,6 +159,13 @@ export function SkillsPage() {
158159

159160
// Builds tab
160161
const [buildsFilter, setBuildsFilter] = useState('')
162+
// Non-suspense on purpose: this only drives the builds toolbar visibility.
163+
// Using useSuspenseQuery here would block the whole Skills page (including
164+
// the Registry/Installed tabs) on the builds endpoint. The actual builds
165+
// table/grid still suspend via their own useSuspenseQuery when the Builds
166+
// tab is active, and the data is shared via react-query's cache.
167+
const { data: buildsData } = useQuery(getApiV1BetaSkillsBuildsOptions())
168+
const hasBuilds = (buildsData?.builds?.length ?? 0) > 0
161169

162170
const hasSkills = skills.length > 0
163171

@@ -223,7 +231,7 @@ export function SkillsPage() {
223231
/>
224232
</>
225233
)}
226-
{tab === 'builds' && (
234+
{tab === 'builds' && hasBuilds && (
227235
<>
228236
<InputSearch
229237
value={buildsFilter}

renderer/src/features/skills/components/table-builds.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
TooltipContent,
2222
TooltipTrigger,
2323
} from '@/common/components/ui/tooltip'
24-
import { HammerIcon, Trash2Icon } from 'lucide-react'
24+
import { HammerIcon, PlusIcon, Trash2Icon } from 'lucide-react'
2525
import { DialogInstallSkill } from './dialog-install-skill'
2626
import { DialogDeleteBuild } from './dialog-delete-build'
2727
import { trackEvent } from '@/common/lib/analytics'
@@ -151,30 +151,31 @@ function BuildRow({ build }: { build: LocalBuild }) {
151151
className="rounded-full"
152152
onClick={(e) => {
153153
e.stopPropagation()
154-
trackEvent('Skills: delete build dialog opened', {
154+
trackEvent('Skills: install dialog opened', {
155155
source: 'build_table',
156156
})
157-
setDeleteOpen(true)
157+
setInstallOpen(true)
158158
}}
159-
aria-label={`Remove ${title}`}
159+
aria-label={`Install ${title}`}
160160
>
161-
<Trash2Icon className="size-4" />
162-
Remove
161+
<PlusIcon className="size-4" />
162+
Install
163163
</Button>
164164
<Button
165165
variant="secondary"
166166
size="sm"
167167
className="rounded-full"
168168
onClick={(e) => {
169169
e.stopPropagation()
170-
trackEvent('Skills: install dialog opened', {
170+
trackEvent('Skills: delete build dialog opened', {
171171
source: 'build_table',
172172
})
173-
setInstallOpen(true)
173+
setDeleteOpen(true)
174174
}}
175-
aria-label={`Install ${title}`}
175+
aria-label={`Remove ${title}`}
176176
>
177-
Install
177+
<Trash2Icon className="size-4" />
178+
Remove
178179
</Button>
179180
</div>
180181
</TableCell>

renderer/src/features/skills/components/table-registry-skills.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from 'react'
22
import { useNavigate } from '@tanstack/react-router'
3-
import { Github } from 'lucide-react'
3+
import { Github, PlusIcon } from 'lucide-react'
44
import type { RegistrySkill } from '@common/api/generated/types.gen'
55
import { Button } from '@/common/components/ui/button'
66
import {
@@ -147,6 +147,7 @@ function RegistrySkillRow({ skill }: { skill: RegistrySkill }) {
147147
}}
148148
aria-label={`Install ${title}`}
149149
>
150+
<PlusIcon className="size-4" />
150151
Install
151152
</Button>
152153
</TableCell>

0 commit comments

Comments
 (0)