@shipfox/client-agent 21.0.0 → 22.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/components/add-custom-provider-card.d.ts.map +1 -1
- package/dist/components/add-custom-provider-card.js +11 -31
- package/dist/components/add-custom-provider-card.js.map +1 -1
- package/dist/components/available-provider-card.d.ts.map +1 -1
- package/dist/components/available-provider-card.js +11 -31
- package/dist/components/available-provider-card.js.map +1 -1
- package/dist/components/available-providers-grid.d.ts +0 -1
- package/dist/components/available-providers-grid.d.ts.map +1 -1
- package/dist/components/available-providers-grid.js +33 -27
- package/dist/components/available-providers-grid.js.map +1 -1
- package/dist/components/harnesses-section.d.ts.map +1 -1
- package/dist/components/harnesses-section.js +152 -146
- package/dist/components/harnesses-section.js.map +1 -1
- package/dist/components/model-provider-grid-skeleton.d.ts +4 -0
- package/dist/components/model-provider-grid-skeleton.d.ts.map +1 -0
- package/dist/components/model-provider-grid-skeleton.js +34 -0
- package/dist/components/model-provider-grid-skeleton.js.map +1 -0
- package/dist/components/model-providers-section.d.ts.map +1 -1
- package/dist/components/model-providers-section.js +268 -281
- package/dist/components/model-providers-section.js.map +1 -1
- package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -1
- package/dist/pages/model-provider-onboarding-page.js +32 -64
- package/dist/pages/model-provider-onboarding-page.js.map +1 -1
- package/dist/routes/agents-settings.js +15 -5
- package/dist/routes/agents-settings.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/add-custom-provider-card.tsx +9 -23
- package/src/components/available-provider-card.tsx +9 -23
- package/src/components/available-providers-grid.tsx +36 -29
- package/src/components/harnesses-section.tsx +116 -109
- package/src/components/model-provider-grid-skeleton.tsx +21 -0
- package/src/components/model-providers-section.test.tsx +2 -2
- package/src/components/model-providers-section.tsx +208 -200
- package/src/pages/model-provider-onboarding-page.tsx +26 -54
- package/src/routes/agents-settings.tsx +7 -3
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-agent",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "22.0.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"@tanstack/react-query": "^5.101.0",
|
|
31
31
|
"@shipfox/api-agent-dto": "12.2.0",
|
|
32
32
|
"@shipfox/client-api": "6.0.1",
|
|
33
|
-
"@shipfox/client-shell": "
|
|
34
|
-
"@shipfox/client-ui": "
|
|
35
|
-
"@shipfox/react-ui": "2.
|
|
33
|
+
"@shipfox/client-shell": "22.0.1",
|
|
34
|
+
"@shipfox/client-ui": "22.0.0",
|
|
35
|
+
"@shipfox/react-ui": "2.1.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -1,32 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {PanelCell, PanelCellAction} from '@shipfox/react-ui/panel';
|
|
2
2
|
import {Text} from '@shipfox/react-ui/typography';
|
|
3
|
-
import {cn} from '@shipfox/react-ui/utils';
|
|
4
|
-
|
|
5
|
-
const SURFACE_CLASS =
|
|
6
|
-
'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
7
3
|
|
|
8
4
|
export function AddCustomProviderCard({onConfigure}: {onConfigure: () => void}) {
|
|
9
5
|
return (
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
className={cn(
|
|
14
|
-
'group block h-full w-full cursor-pointer p-panel-compact text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
|
|
15
|
-
SURFACE_CLASS,
|
|
16
|
-
)}
|
|
6
|
+
<PanelCell>
|
|
7
|
+
<PanelCellAction
|
|
8
|
+
action="Configure"
|
|
17
9
|
aria-label="Configure custom provider"
|
|
18
10
|
onClick={onConfigure}
|
|
19
11
|
>
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<Text size="sm">Configure</Text>
|
|
26
|
-
<Icon name="chevronRight" className="size-16" />
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
</button>
|
|
30
|
-
</li>
|
|
12
|
+
<Text as="span" size="md" bold className="min-w-0 truncate">
|
|
13
|
+
Custom
|
|
14
|
+
</Text>
|
|
15
|
+
</PanelCellAction>
|
|
16
|
+
</PanelCell>
|
|
31
17
|
);
|
|
32
18
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {PanelCell, PanelCellAction} from '@shipfox/react-ui/panel';
|
|
2
2
|
import {Text} from '@shipfox/react-ui/typography';
|
|
3
|
-
import {cn} from '@shipfox/react-ui/utils';
|
|
4
3
|
import type {SupportedProvider} from '#core/models.js';
|
|
5
4
|
|
|
6
|
-
const SURFACE_CLASS =
|
|
7
|
-
'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
8
|
-
|
|
9
5
|
export function AvailableProviderCard({
|
|
10
6
|
entry,
|
|
11
7
|
onConfigure,
|
|
@@ -14,26 +10,16 @@ export function AvailableProviderCard({
|
|
|
14
10
|
onConfigure: () => void;
|
|
15
11
|
}) {
|
|
16
12
|
return (
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
className={cn(
|
|
21
|
-
'group block w-full cursor-pointer p-panel-compact text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
|
|
22
|
-
SURFACE_CLASS,
|
|
23
|
-
)}
|
|
13
|
+
<PanelCell>
|
|
14
|
+
<PanelCellAction
|
|
15
|
+
action="Configure"
|
|
24
16
|
aria-label={`Configure ${entry.label}`}
|
|
25
17
|
onClick={onConfigure}
|
|
26
18
|
>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<Text size="sm">Configure</Text>
|
|
33
|
-
<Icon name="chevronRight" className="size-16" />
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</button>
|
|
37
|
-
</li>
|
|
19
|
+
<Text as="span" size="md" bold className="min-w-0 truncate">
|
|
20
|
+
{entry.label}
|
|
21
|
+
</Text>
|
|
22
|
+
</PanelCellAction>
|
|
23
|
+
</PanelCell>
|
|
38
24
|
);
|
|
39
25
|
}
|
|
@@ -2,14 +2,13 @@ import {Button} from '@shipfox/react-ui/button';
|
|
|
2
2
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
3
3
|
import {Icon} from '@shipfox/react-ui/icon';
|
|
4
4
|
import {Input} from '@shipfox/react-ui/input';
|
|
5
|
+
import {Panel, PanelBody, PanelGrid, PanelHeader} from '@shipfox/react-ui/panel';
|
|
5
6
|
import type {ReactNode} from 'react';
|
|
6
7
|
import {useMemo, useRef, useState} from 'react';
|
|
7
8
|
import type {SupportedProvider} from '#core/models.js';
|
|
8
9
|
import {providerMatchesSearch} from '#core/provider-policy.js';
|
|
9
10
|
import {AvailableProviderCard} from './available-provider-card.js';
|
|
10
11
|
|
|
11
|
-
export const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1';
|
|
12
|
-
|
|
13
12
|
const SEARCH_VISIBILITY_THRESHOLD = 8;
|
|
14
13
|
const MAX_ECHOED_QUERY_LENGTH = 40;
|
|
15
14
|
|
|
@@ -45,38 +44,46 @@ export function AvailableProvidersGrid<TEntry extends SupportedProvider>({
|
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
return (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<ul className={PROVIDER_GRID_CLASS} aria-label={providerListLabel}>
|
|
63
|
-
{filteredEntries.map((entry) => (
|
|
64
|
-
<AvailableProviderCard
|
|
65
|
-
key={entry.id}
|
|
66
|
-
entry={entry}
|
|
67
|
-
onConfigure={() => onSelect(entry)}
|
|
47
|
+
<>
|
|
48
|
+
<Panel>
|
|
49
|
+
{showSearch ? (
|
|
50
|
+
<PanelHeader>
|
|
51
|
+
<Input
|
|
52
|
+
ref={inputRef}
|
|
53
|
+
type="search"
|
|
54
|
+
aria-label="Search providers"
|
|
55
|
+
placeholder="Search providers..."
|
|
56
|
+
value={search}
|
|
57
|
+
iconLeft={
|
|
58
|
+
<Icon name="searchLine" className="size-16 text-foreground-neutral-muted" />
|
|
59
|
+
}
|
|
60
|
+
onChange={(event) => setSearch(event.target.value)}
|
|
68
61
|
/>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
</PanelHeader>
|
|
63
|
+
) : null}
|
|
64
|
+
|
|
65
|
+
<PanelBody>
|
|
66
|
+
{visibleCount > 0 ? (
|
|
67
|
+
<PanelGrid aria-label={providerListLabel}>
|
|
68
|
+
{filteredEntries.map((entry) => (
|
|
69
|
+
<AvailableProviderCard
|
|
70
|
+
key={entry.id}
|
|
71
|
+
entry={entry}
|
|
72
|
+
onConfigure={() => onSelect(entry)}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
75
|
+
{trailingCardVisible ? trailingCard : null}
|
|
76
|
+
</PanelGrid>
|
|
77
|
+
) : (
|
|
78
|
+
<NoProviderSearchResults search={trimmedSearch} onClear={clearSearch} />
|
|
79
|
+
)}
|
|
80
|
+
</PanelBody>
|
|
81
|
+
</Panel>
|
|
75
82
|
|
|
76
83
|
<p role="status" aria-live="polite" className="sr-only">
|
|
77
84
|
{resultCountText}
|
|
78
85
|
</p>
|
|
79
|
-
|
|
86
|
+
</>
|
|
80
87
|
);
|
|
81
88
|
}
|
|
82
89
|
|
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
DropdownMenuTrigger,
|
|
9
9
|
} from '@shipfox/react-ui/dropdown-menu';
|
|
10
10
|
import {Icon} from '@shipfox/react-ui/icon';
|
|
11
|
+
import {Panel, PanelBody, PanelRow} from '@shipfox/react-ui/panel';
|
|
11
12
|
import {Skeleton} from '@shipfox/react-ui/skeleton';
|
|
12
13
|
import {toast} from '@shipfox/react-ui/toast';
|
|
13
14
|
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
14
15
|
import {Header, Text} from '@shipfox/react-ui/typography';
|
|
15
|
-
import {cn} from '@shipfox/react-ui/utils';
|
|
16
16
|
import {useRef, useState} from 'react';
|
|
17
17
|
import {DEFAULT_HARNESS, listHarnesses} from '#core/harness-policy.js';
|
|
18
18
|
import type {HarnessDescriptor, HarnessId} from '#core/models.js';
|
|
@@ -23,9 +23,6 @@ import {
|
|
|
23
23
|
import {modelProviderConfigErrorToFormError} from './form-errors.js';
|
|
24
24
|
import {isHarnessAvailable} from './harness-availability.js';
|
|
25
25
|
|
|
26
|
-
const SURFACE_CLASS =
|
|
27
|
-
'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
28
|
-
|
|
29
26
|
export function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string}) {
|
|
30
27
|
const configsQuery = useModelProviderConfigsQuery(workspaceId);
|
|
31
28
|
const setDefaultHarness = useSetDefaultHarnessMutation();
|
|
@@ -87,37 +84,39 @@ export function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string})
|
|
|
87
84
|
<section className="flex flex-col gap-group" aria-label="Harnesses">
|
|
88
85
|
<div className="flex flex-col gap-tight">
|
|
89
86
|
<Header variant="h3">Harnesses</Header>
|
|
90
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
91
|
-
Harnesses available to run agent steps in this workspace.
|
|
92
|
-
</Text>
|
|
93
87
|
</div>
|
|
94
88
|
|
|
95
89
|
{configsQuery.isPending ? <HarnessRowsSkeleton /> : null}
|
|
96
90
|
|
|
97
91
|
{configsQuery.isError && configsQuery.data === undefined ? (
|
|
98
|
-
<
|
|
99
|
-
<QueryLoadError query={configsQuery} subject="harnesses" />
|
|
100
|
-
</
|
|
92
|
+
<Panel>
|
|
93
|
+
<QueryLoadError query={configsQuery} subject="harnesses" variant="panel" />
|
|
94
|
+
</Panel>
|
|
101
95
|
) : null}
|
|
102
96
|
|
|
103
97
|
{configsQuery.data !== undefined ? (
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
98
|
+
<Panel>
|
|
99
|
+
<PanelBody asChild>
|
|
100
|
+
<ul>
|
|
101
|
+
{listHarnesses().map((harness) => (
|
|
102
|
+
<HarnessRow
|
|
103
|
+
key={harness.id}
|
|
104
|
+
harness={harness}
|
|
105
|
+
isDefault={harness.id === defaultHarnessId}
|
|
106
|
+
isAvailable={isHarnessAvailable(harness, configs)}
|
|
107
|
+
isSettingDefault={pendingDefaultHarness?.workspaceId === workspaceId}
|
|
108
|
+
defaultError={
|
|
109
|
+
defaultError?.workspaceId === workspaceId &&
|
|
110
|
+
defaultError.harnessId === harness.id
|
|
111
|
+
? defaultError.message
|
|
112
|
+
: undefined
|
|
113
|
+
}
|
|
114
|
+
onSetDefault={handleSetDefault}
|
|
115
|
+
/>
|
|
116
|
+
))}
|
|
117
|
+
</ul>
|
|
118
|
+
</PanelBody>
|
|
119
|
+
</Panel>
|
|
121
120
|
) : null}
|
|
122
121
|
</section>
|
|
123
122
|
);
|
|
@@ -141,78 +140,80 @@ function HarnessRow({
|
|
|
141
140
|
const unavailableCopy = harnessUnavailableCopy(isDefault);
|
|
142
141
|
|
|
143
142
|
return (
|
|
144
|
-
<
|
|
145
|
-
<
|
|
146
|
-
<div className="flex
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<
|
|
151
|
-
<
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<
|
|
171
|
-
<
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
143
|
+
<PanelRow asChild className="flex-col items-stretch gap-inline">
|
|
144
|
+
<li>
|
|
145
|
+
<div className="flex items-center justify-between gap-cluster">
|
|
146
|
+
<div className="flex min-w-0 items-center gap-inline">
|
|
147
|
+
{isDefault ? (
|
|
148
|
+
<>
|
|
149
|
+
<Tooltip>
|
|
150
|
+
<TooltipTrigger asChild>
|
|
151
|
+
<span className="inline-flex size-16 shrink-0 items-center justify-center">
|
|
152
|
+
<Icon
|
|
153
|
+
name="starLine"
|
|
154
|
+
className="size-16 text-foreground-neutral-muted"
|
|
155
|
+
aria-hidden
|
|
156
|
+
/>
|
|
157
|
+
</span>
|
|
158
|
+
</TooltipTrigger>
|
|
159
|
+
<TooltipContent>Default harness</TooltipContent>
|
|
160
|
+
</Tooltip>
|
|
161
|
+
<span className="sr-only">Default harness</span>
|
|
162
|
+
</>
|
|
163
|
+
) : null}
|
|
164
|
+
<Text size="md" bold className="truncate">
|
|
165
|
+
{harness.label}
|
|
166
|
+
</Text>
|
|
167
|
+
{!isAvailable ? (
|
|
168
|
+
<>
|
|
169
|
+
<Tooltip>
|
|
170
|
+
<TooltipTrigger asChild>
|
|
171
|
+
<span className="inline-flex size-16 shrink-0 items-center justify-center">
|
|
172
|
+
<Icon
|
|
173
|
+
name="errorWarningLine"
|
|
174
|
+
className="size-16 text-foreground-warning-base"
|
|
175
|
+
aria-hidden
|
|
176
|
+
/>
|
|
177
|
+
</span>
|
|
178
|
+
</TooltipTrigger>
|
|
179
|
+
<TooltipContent>{unavailableCopy}</TooltipContent>
|
|
180
|
+
</Tooltip>
|
|
181
|
+
<span className="sr-only">{unavailableCopy}</span>
|
|
182
|
+
</>
|
|
183
|
+
) : null}
|
|
184
|
+
</div>
|
|
185
|
+
{!isDefault ? (
|
|
186
|
+
<DropdownMenu>
|
|
187
|
+
<DropdownMenuTrigger asChild>
|
|
188
|
+
<IconButton
|
|
189
|
+
size="sm"
|
|
190
|
+
variant="transparent"
|
|
191
|
+
icon="more2Line"
|
|
192
|
+
disabled={isSettingDefault}
|
|
193
|
+
aria-label={`Open ${harness.label} harness actions`}
|
|
194
|
+
/>
|
|
195
|
+
</DropdownMenuTrigger>
|
|
196
|
+
<DropdownMenuContent align="end">
|
|
197
|
+
<DropdownMenuItem
|
|
198
|
+
icon="starLine"
|
|
199
|
+
disabled={isSettingDefault || !isAvailable}
|
|
200
|
+
onSelect={() => {
|
|
201
|
+
onSetDefault(harness);
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
Set as default
|
|
205
|
+
</DropdownMenuItem>
|
|
206
|
+
</DropdownMenuContent>
|
|
207
|
+
</DropdownMenu>
|
|
183
208
|
) : null}
|
|
184
209
|
</div>
|
|
185
|
-
{
|
|
186
|
-
<
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
size="sm"
|
|
190
|
-
variant="transparent"
|
|
191
|
-
icon="more2Line"
|
|
192
|
-
disabled={isSettingDefault}
|
|
193
|
-
aria-label={`Open ${harness.label} harness actions`}
|
|
194
|
-
/>
|
|
195
|
-
</DropdownMenuTrigger>
|
|
196
|
-
<DropdownMenuContent align="end">
|
|
197
|
-
<DropdownMenuItem
|
|
198
|
-
icon="starLine"
|
|
199
|
-
disabled={isSettingDefault || !isAvailable}
|
|
200
|
-
onSelect={() => {
|
|
201
|
-
onSetDefault(harness);
|
|
202
|
-
}}
|
|
203
|
-
>
|
|
204
|
-
Set as default
|
|
205
|
-
</DropdownMenuItem>
|
|
206
|
-
</DropdownMenuContent>
|
|
207
|
-
</DropdownMenu>
|
|
210
|
+
{defaultError ? (
|
|
211
|
+
<Callout role="alert" type="error">
|
|
212
|
+
<Text size="sm">{defaultError}</Text>
|
|
213
|
+
</Callout>
|
|
208
214
|
) : null}
|
|
209
|
-
</
|
|
210
|
-
|
|
211
|
-
<Callout role="alert" type="error">
|
|
212
|
-
<Text size="sm">{defaultError}</Text>
|
|
213
|
-
</Callout>
|
|
214
|
-
) : null}
|
|
215
|
-
</li>
|
|
215
|
+
</li>
|
|
216
|
+
</PanelRow>
|
|
216
217
|
);
|
|
217
218
|
}
|
|
218
219
|
|
|
@@ -224,18 +225,24 @@ function harnessUnavailableCopy(isDefault: boolean): string {
|
|
|
224
225
|
|
|
225
226
|
function HarnessRowsSkeleton() {
|
|
226
227
|
return (
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
228
|
+
<Panel>
|
|
229
|
+
<PanelBody asChild>
|
|
230
|
+
<ul role="status" aria-label="Loading harnesses">
|
|
231
|
+
{[0, 1].map((row) => (
|
|
232
|
+
<PanelRow
|
|
233
|
+
asChild
|
|
234
|
+
className="justify-start gap-cluster hover:bg-background-neutral-base"
|
|
235
|
+
key={row}
|
|
236
|
+
>
|
|
237
|
+
<li>
|
|
238
|
+
<Skeleton className="size-16 shrink-0" />
|
|
239
|
+
<Skeleton className="h-16 w-120" />
|
|
240
|
+
<Skeleton className="ml-auto size-28 shrink-0" />
|
|
241
|
+
</li>
|
|
242
|
+
</PanelRow>
|
|
243
|
+
))}
|
|
244
|
+
</ul>
|
|
245
|
+
</PanelBody>
|
|
246
|
+
</Panel>
|
|
240
247
|
);
|
|
241
248
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {Panel, PanelBody, PanelCell, PanelGrid} from '@shipfox/react-ui/panel';
|
|
2
|
+
import {Skeleton} from '@shipfox/react-ui/skeleton';
|
|
3
|
+
|
|
4
|
+
export function ModelProviderGridSkeleton({label}: {label: string}) {
|
|
5
|
+
return (
|
|
6
|
+
<Panel>
|
|
7
|
+
<PanelBody>
|
|
8
|
+
<PanelGrid role="status" aria-busy="true" aria-label={label}>
|
|
9
|
+
{[0, 1, 2, 3].map((tile) => (
|
|
10
|
+
<PanelCell key={tile}>
|
|
11
|
+
<div className="flex items-center justify-between gap-cluster px-row py-row">
|
|
12
|
+
<Skeleton className="h-16 w-100" />
|
|
13
|
+
<Skeleton className="h-16 w-64 shrink-0" />
|
|
14
|
+
</div>
|
|
15
|
+
</PanelCell>
|
|
16
|
+
))}
|
|
17
|
+
</PanelGrid>
|
|
18
|
+
</PanelBody>
|
|
19
|
+
</Panel>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -89,8 +89,8 @@ describe('WorkspaceModelProvidersSection', () => {
|
|
|
89
89
|
expect(screen.getByText('Default provider')).toHaveClass('sr-only');
|
|
90
90
|
expect(screen.getByText('Available providers')).toBeVisible();
|
|
91
91
|
expect(
|
|
92
|
-
screen.
|
|
93
|
-
).
|
|
92
|
+
screen.queryByText('Providers that can be configured for agent steps in this workspace.'),
|
|
93
|
+
).not.toBeInTheDocument();
|
|
94
94
|
expect(screen.getByText('OpenAI')).toBeVisible();
|
|
95
95
|
const customProviderButton = screen.getByRole('button', {name: 'Configure custom provider'});
|
|
96
96
|
expect(within(customProviderButton).getByText('Custom')).toBeVisible();
|