@shipfox/client-agent 21.0.0 → 22.0.0

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.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/dist/components/add-custom-provider-card.d.ts.map +1 -1
  4. package/dist/components/add-custom-provider-card.js +11 -31
  5. package/dist/components/add-custom-provider-card.js.map +1 -1
  6. package/dist/components/available-provider-card.d.ts.map +1 -1
  7. package/dist/components/available-provider-card.js +11 -31
  8. package/dist/components/available-provider-card.js.map +1 -1
  9. package/dist/components/available-providers-grid.d.ts +0 -1
  10. package/dist/components/available-providers-grid.d.ts.map +1 -1
  11. package/dist/components/available-providers-grid.js +33 -27
  12. package/dist/components/available-providers-grid.js.map +1 -1
  13. package/dist/components/harnesses-section.d.ts.map +1 -1
  14. package/dist/components/harnesses-section.js +147 -134
  15. package/dist/components/harnesses-section.js.map +1 -1
  16. package/dist/components/model-provider-grid-skeleton.d.ts +4 -0
  17. package/dist/components/model-provider-grid-skeleton.d.ts.map +1 -0
  18. package/dist/components/model-provider-grid-skeleton.js +34 -0
  19. package/dist/components/model-provider-grid-skeleton.js.map +1 -0
  20. package/dist/components/model-providers-section.d.ts.map +1 -1
  21. package/dist/components/model-providers-section.js +253 -245
  22. package/dist/components/model-providers-section.js.map +1 -1
  23. package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -1
  24. package/dist/pages/model-provider-onboarding-page.js +32 -64
  25. package/dist/pages/model-provider-onboarding-page.js.map +1 -1
  26. package/dist/tsconfig.test.tsbuildinfo +1 -1
  27. package/package.json +4 -4
  28. package/src/components/add-custom-provider-card.tsx +9 -23
  29. package/src/components/available-provider-card.tsx +9 -23
  30. package/src/components/available-providers-grid.tsx +36 -29
  31. package/src/components/harnesses-section.tsx +116 -106
  32. package/src/components/model-provider-grid-skeleton.tsx +21 -0
  33. package/src/components/model-providers-section.tsx +208 -191
  34. package/src/pages/model-provider-onboarding-page.tsx +26 -54
  35. package/tsconfig.build.tsbuildinfo +1 -1
@@ -4,17 +4,16 @@ import { IconButton } from '@shipfox/react-ui/button';
4
4
  import { Callout } from '@shipfox/react-ui/callout';
5
5
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
6
6
  import { Icon } from '@shipfox/react-ui/icon';
7
+ import { Panel, PanelBody, PanelRow } from '@shipfox/react-ui/panel';
7
8
  import { Skeleton } from '@shipfox/react-ui/skeleton';
8
9
  import { toast } from '@shipfox/react-ui/toast';
9
10
  import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
10
11
  import { Header, Text } from '@shipfox/react-ui/typography';
11
- import { cn } from '@shipfox/react-ui/utils';
12
12
  import { useRef, useState } from 'react';
13
13
  import { DEFAULT_HARNESS, listHarnesses } from '#core/harness-policy.js';
14
14
  import { useModelProviderConfigsQuery, useSetDefaultHarnessMutation } from '#hooks/api/model-providers.js';
15
15
  import { modelProviderConfigErrorToFormError } from './form-errors.js';
16
16
  import { isHarnessAvailable } from './harness-availability.js';
17
- const SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
18
17
  export function WorkspaceHarnessesSection({ workspaceId }) {
19
18
  const configsQuery = useModelProviderConfigsQuery(workspaceId);
20
19
  const setDefaultHarness = useSetDefaultHarnessMutation();
@@ -84,134 +83,141 @@ export function WorkspaceHarnessesSection({ workspaceId }) {
84
83
  ]
85
84
  }),
86
85
  configsQuery.isPending ? /*#__PURE__*/ _jsx(HarnessRowsSkeleton, {}) : null,
87
- configsQuery.isError && configsQuery.data === undefined ? /*#__PURE__*/ _jsx("div", {
88
- className: cn(SURFACE_CLASS, 'px-row'),
86
+ configsQuery.isError && configsQuery.data === undefined ? /*#__PURE__*/ _jsx(Panel, {
89
87
  children: /*#__PURE__*/ _jsx(QueryLoadError, {
90
88
  query: configsQuery,
91
- subject: "harnesses"
89
+ subject: "harnesses",
90
+ variant: "panel"
92
91
  })
93
92
  }) : null,
94
- configsQuery.data !== undefined ? /*#__PURE__*/ _jsx("ul", {
95
- className: cn('divide-y divide-border-neutral-base', SURFACE_CLASS),
96
- children: listHarnesses().map((harness)=>/*#__PURE__*/ _jsx(HarnessRow, {
97
- harness: harness,
98
- isDefault: harness.id === defaultHarnessId,
99
- isAvailable: isHarnessAvailable(harness, configs),
100
- isSettingDefault: pendingDefaultHarness?.workspaceId === workspaceId,
101
- defaultError: defaultError?.workspaceId === workspaceId && defaultError.harnessId === harness.id ? defaultError.message : undefined,
102
- onSetDefault: handleSetDefault
103
- }, harness.id))
93
+ configsQuery.data !== undefined ? /*#__PURE__*/ _jsx(Panel, {
94
+ children: /*#__PURE__*/ _jsx(PanelBody, {
95
+ asChild: true,
96
+ children: /*#__PURE__*/ _jsx("ul", {
97
+ children: listHarnesses().map((harness)=>/*#__PURE__*/ _jsx(HarnessRow, {
98
+ harness: harness,
99
+ isDefault: harness.id === defaultHarnessId,
100
+ isAvailable: isHarnessAvailable(harness, configs),
101
+ isSettingDefault: pendingDefaultHarness?.workspaceId === workspaceId,
102
+ defaultError: defaultError?.workspaceId === workspaceId && defaultError.harnessId === harness.id ? defaultError.message : undefined,
103
+ onSetDefault: handleSetDefault
104
+ }, harness.id))
105
+ })
106
+ })
104
107
  }) : null
105
108
  ]
106
109
  });
107
110
  }
108
111
  function HarnessRow({ harness, isDefault, isAvailable, isSettingDefault, defaultError, onSetDefault }) {
109
112
  const unavailableCopy = harnessUnavailableCopy(isDefault);
110
- return /*#__PURE__*/ _jsxs("li", {
111
- className: "flex flex-col gap-inline px-row py-row transition-colors hover:bg-background-components-hover",
112
- children: [
113
- /*#__PURE__*/ _jsxs("div", {
114
- className: "flex items-center justify-between gap-cluster",
115
- children: [
116
- /*#__PURE__*/ _jsxs("div", {
117
- className: "flex min-w-0 items-center gap-inline",
118
- children: [
119
- isDefault ? /*#__PURE__*/ _jsxs(_Fragment, {
120
- children: [
121
- /*#__PURE__*/ _jsxs(Tooltip, {
122
- children: [
123
- /*#__PURE__*/ _jsx(TooltipTrigger, {
124
- asChild: true,
125
- children: /*#__PURE__*/ _jsx("span", {
126
- className: "inline-flex size-16 shrink-0 items-center justify-center",
127
- children: /*#__PURE__*/ _jsx(Icon, {
128
- name: "starLine",
129
- className: "size-16 text-foreground-neutral-muted",
130
- "aria-hidden": true
113
+ return /*#__PURE__*/ _jsx(PanelRow, {
114
+ asChild: true,
115
+ className: "flex-col items-stretch gap-inline",
116
+ children: /*#__PURE__*/ _jsxs("li", {
117
+ children: [
118
+ /*#__PURE__*/ _jsxs("div", {
119
+ className: "flex items-center justify-between gap-cluster",
120
+ children: [
121
+ /*#__PURE__*/ _jsxs("div", {
122
+ className: "flex min-w-0 items-center gap-inline",
123
+ children: [
124
+ isDefault ? /*#__PURE__*/ _jsxs(_Fragment, {
125
+ children: [
126
+ /*#__PURE__*/ _jsxs(Tooltip, {
127
+ children: [
128
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
129
+ asChild: true,
130
+ children: /*#__PURE__*/ _jsx("span", {
131
+ className: "inline-flex size-16 shrink-0 items-center justify-center",
132
+ children: /*#__PURE__*/ _jsx(Icon, {
133
+ name: "starLine",
134
+ className: "size-16 text-foreground-neutral-muted",
135
+ "aria-hidden": true
136
+ })
131
137
  })
138
+ }),
139
+ /*#__PURE__*/ _jsx(TooltipContent, {
140
+ children: "Default harness"
132
141
  })
133
- }),
134
- /*#__PURE__*/ _jsx(TooltipContent, {
135
- children: "Default harness"
136
- })
137
- ]
138
- }),
139
- /*#__PURE__*/ _jsx("span", {
140
- className: "sr-only",
141
- children: "Default harness"
142
- })
143
- ]
144
- }) : null,
145
- /*#__PURE__*/ _jsx(Text, {
146
- size: "md",
147
- bold: true,
148
- className: "truncate",
149
- children: harness.label
150
- }),
151
- !isAvailable ? /*#__PURE__*/ _jsxs(_Fragment, {
152
- children: [
153
- /*#__PURE__*/ _jsxs(Tooltip, {
154
- children: [
155
- /*#__PURE__*/ _jsx(TooltipTrigger, {
156
- asChild: true,
157
- children: /*#__PURE__*/ _jsx("span", {
158
- className: "inline-flex size-16 shrink-0 items-center justify-center",
159
- children: /*#__PURE__*/ _jsx(Icon, {
160
- name: "errorWarningLine",
161
- className: "size-16 text-foreground-warning-base",
162
- "aria-hidden": true
142
+ ]
143
+ }),
144
+ /*#__PURE__*/ _jsx("span", {
145
+ className: "sr-only",
146
+ children: "Default harness"
147
+ })
148
+ ]
149
+ }) : null,
150
+ /*#__PURE__*/ _jsx(Text, {
151
+ size: "md",
152
+ bold: true,
153
+ className: "truncate",
154
+ children: harness.label
155
+ }),
156
+ !isAvailable ? /*#__PURE__*/ _jsxs(_Fragment, {
157
+ children: [
158
+ /*#__PURE__*/ _jsxs(Tooltip, {
159
+ children: [
160
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
161
+ asChild: true,
162
+ children: /*#__PURE__*/ _jsx("span", {
163
+ className: "inline-flex size-16 shrink-0 items-center justify-center",
164
+ children: /*#__PURE__*/ _jsx(Icon, {
165
+ name: "errorWarningLine",
166
+ className: "size-16 text-foreground-warning-base",
167
+ "aria-hidden": true
168
+ })
163
169
  })
170
+ }),
171
+ /*#__PURE__*/ _jsx(TooltipContent, {
172
+ children: unavailableCopy
164
173
  })
165
- }),
166
- /*#__PURE__*/ _jsx(TooltipContent, {
167
- children: unavailableCopy
168
- })
169
- ]
170
- }),
171
- /*#__PURE__*/ _jsx("span", {
172
- className: "sr-only",
173
- children: unavailableCopy
174
+ ]
175
+ }),
176
+ /*#__PURE__*/ _jsx("span", {
177
+ className: "sr-only",
178
+ children: unavailableCopy
179
+ })
180
+ ]
181
+ }) : null
182
+ ]
183
+ }),
184
+ !isDefault ? /*#__PURE__*/ _jsxs(DropdownMenu, {
185
+ children: [
186
+ /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
187
+ asChild: true,
188
+ children: /*#__PURE__*/ _jsx(IconButton, {
189
+ size: "sm",
190
+ variant: "transparent",
191
+ icon: "more2Line",
192
+ disabled: isSettingDefault,
193
+ "aria-label": `Open ${harness.label} harness actions`
194
+ })
195
+ }),
196
+ /*#__PURE__*/ _jsx(DropdownMenuContent, {
197
+ align: "end",
198
+ children: /*#__PURE__*/ _jsx(DropdownMenuItem, {
199
+ icon: "starLine",
200
+ disabled: isSettingDefault || !isAvailable,
201
+ onSelect: ()=>{
202
+ onSetDefault(harness);
203
+ },
204
+ children: "Set as default"
174
205
  })
175
- ]
176
- }) : null
177
- ]
178
- }),
179
- !isDefault ? /*#__PURE__*/ _jsxs(DropdownMenu, {
180
- children: [
181
- /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
182
- asChild: true,
183
- children: /*#__PURE__*/ _jsx(IconButton, {
184
- size: "sm",
185
- variant: "transparent",
186
- icon: "more2Line",
187
- disabled: isSettingDefault,
188
- "aria-label": `Open ${harness.label} harness actions`
189
- })
190
- }),
191
- /*#__PURE__*/ _jsx(DropdownMenuContent, {
192
- align: "end",
193
- children: /*#__PURE__*/ _jsx(DropdownMenuItem, {
194
- icon: "starLine",
195
- disabled: isSettingDefault || !isAvailable,
196
- onSelect: ()=>{
197
- onSetDefault(harness);
198
- },
199
- children: "Set as default"
200
206
  })
201
- })
202
- ]
203
- }) : null
204
- ]
205
- }),
206
- defaultError ? /*#__PURE__*/ _jsx(Callout, {
207
- role: "alert",
208
- type: "error",
209
- children: /*#__PURE__*/ _jsx(Text, {
210
- size: "sm",
211
- children: defaultError
212
- })
213
- }) : null
214
- ]
207
+ ]
208
+ }) : null
209
+ ]
210
+ }),
211
+ defaultError ? /*#__PURE__*/ _jsx(Callout, {
212
+ role: "alert",
213
+ type: "error",
214
+ children: /*#__PURE__*/ _jsx(Text, {
215
+ size: "sm",
216
+ children: defaultError
217
+ })
218
+ }) : null
219
+ ]
220
+ })
215
221
  });
216
222
  }
217
223
  function harnessUnavailableCopy(isDefault) {
@@ -220,27 +226,34 @@ function harnessUnavailableCopy(isDefault) {
220
226
  return `${base} The stored default harness cannot currently run in this workspace.`;
221
227
  }
222
228
  function HarnessRowsSkeleton() {
223
- return /*#__PURE__*/ _jsx("ul", {
224
- role: "status",
225
- "aria-label": "Loading harnesses",
226
- className: cn('divide-y divide-border-neutral-base', SURFACE_CLASS),
227
- children: [
228
- 0,
229
- 1
230
- ].map((row)=>/*#__PURE__*/ _jsxs("li", {
231
- className: "flex items-center gap-cluster px-row py-row",
229
+ return /*#__PURE__*/ _jsx(Panel, {
230
+ children: /*#__PURE__*/ _jsx(PanelBody, {
231
+ asChild: true,
232
+ children: /*#__PURE__*/ _jsx("ul", {
233
+ role: "status",
234
+ "aria-label": "Loading harnesses",
232
235
  children: [
233
- /*#__PURE__*/ _jsx(Skeleton, {
234
- className: "size-16 shrink-0"
235
- }),
236
- /*#__PURE__*/ _jsx(Skeleton, {
237
- className: "h-16 w-120"
238
- }),
239
- /*#__PURE__*/ _jsx(Skeleton, {
240
- className: "ml-auto size-28 shrink-0"
241
- })
242
- ]
243
- }, row))
236
+ 0,
237
+ 1
238
+ ].map((row)=>/*#__PURE__*/ _jsx(PanelRow, {
239
+ asChild: true,
240
+ className: "justify-start gap-cluster hover:bg-background-neutral-base",
241
+ children: /*#__PURE__*/ _jsxs("li", {
242
+ children: [
243
+ /*#__PURE__*/ _jsx(Skeleton, {
244
+ className: "size-16 shrink-0"
245
+ }),
246
+ /*#__PURE__*/ _jsx(Skeleton, {
247
+ className: "h-16 w-120"
248
+ }),
249
+ /*#__PURE__*/ _jsx(Skeleton, {
250
+ className: "ml-auto size-28 shrink-0"
251
+ })
252
+ ]
253
+ })
254
+ }, row))
255
+ })
256
+ })
244
257
  });
245
258
  }
246
259
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/harnesses-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useRef, useState} from 'react';\nimport {DEFAULT_HARNESS, listHarnesses} from '#core/harness-policy.js';\nimport type {HarnessDescriptor, HarnessId} from '#core/models.js';\nimport {\n useModelProviderConfigsQuery,\n useSetDefaultHarnessMutation,\n} from '#hooks/api/model-providers.js';\nimport {modelProviderConfigErrorToFormError} from './form-errors.js';\nimport {isHarnessAvailable} from './harness-availability.js';\n\nconst SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string}) {\n const configsQuery = useModelProviderConfigsQuery(workspaceId);\n const setDefaultHarness = useSetDefaultHarnessMutation();\n const activeWorkspaceIdRef = useRef(workspaceId);\n const defaultRequestSeqRef = useRef(0);\n const activeDefaultRequestRef = useRef<{workspaceId: string; id: number} | null>(null);\n const [pendingDefaultHarness, setPendingDefaultHarness] = useState<{\n workspaceId: string;\n harnessId: HarnessId;\n } | null>(null);\n const [defaultError, setDefaultError] = useState<\n {workspaceId: string; harnessId: HarnessId; message: string} | undefined\n >();\n const configs = configsQuery.data?.configs ?? [];\n const defaultHarnessId = configsQuery.data?.defaultHarnessId ?? DEFAULT_HARNESS;\n activeWorkspaceIdRef.current = workspaceId;\n\n function isActiveDefaultRequest(request: {workspaceId: string; id: number}) {\n const activeRequest = activeDefaultRequestRef.current;\n return (\n activeWorkspaceIdRef.current === request.workspaceId &&\n activeRequest?.workspaceId === request.workspaceId &&\n activeRequest.id === request.id\n );\n }\n\n async function handleSetDefault(harness: HarnessDescriptor) {\n if (activeDefaultRequestRef.current?.workspaceId === workspaceId) return;\n\n const request = {workspaceId, id: defaultRequestSeqRef.current + 1};\n defaultRequestSeqRef.current = request.id;\n activeDefaultRequestRef.current = request;\n setPendingDefaultHarness({workspaceId, harnessId: harness.id});\n setDefaultError(undefined);\n try {\n await setDefaultHarness.mutateAsync({\n workspaceId,\n harnessId: harness.id,\n });\n if (!isActiveDefaultRequest(request)) return;\n toast.success(`${harness.label} is now the default harness`);\n } catch (error) {\n if (!isActiveDefaultRequest(request)) return;\n const mapped = modelProviderConfigErrorToFormError(error);\n setDefaultError({\n workspaceId,\n harnessId: harness.id,\n message: mapped.message || 'Could not save default harness. Try again.',\n });\n } finally {\n if (isActiveDefaultRequest(request)) {\n activeDefaultRequestRef.current = null;\n setPendingDefaultHarness(null);\n }\n }\n }\n\n return (\n <section className=\"flex flex-col gap-group\" aria-label=\"Harnesses\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Harnesses</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Harnesses available to run agent steps in this workspace.\n </Text>\n </div>\n\n {configsQuery.isPending ? <HarnessRowsSkeleton /> : null}\n\n {configsQuery.isError && configsQuery.data === undefined ? (\n <div className={cn(SURFACE_CLASS, 'px-row')}>\n <QueryLoadError query={configsQuery} subject=\"harnesses\" />\n </div>\n ) : null}\n\n {configsQuery.data !== undefined ? (\n <ul className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}>\n {listHarnesses().map((harness) => (\n <HarnessRow\n key={harness.id}\n harness={harness}\n isDefault={harness.id === defaultHarnessId}\n isAvailable={isHarnessAvailable(harness, configs)}\n isSettingDefault={pendingDefaultHarness?.workspaceId === workspaceId}\n defaultError={\n defaultError?.workspaceId === workspaceId && defaultError.harnessId === harness.id\n ? defaultError.message\n : undefined\n }\n onSetDefault={handleSetDefault}\n />\n ))}\n </ul>\n ) : null}\n </section>\n );\n}\n\nfunction HarnessRow({\n harness,\n isDefault,\n isAvailable,\n isSettingDefault,\n defaultError,\n onSetDefault,\n}: {\n harness: HarnessDescriptor;\n isDefault: boolean;\n isAvailable: boolean;\n isSettingDefault: boolean;\n defaultError: string | undefined;\n onSetDefault: (harness: HarnessDescriptor) => void;\n}) {\n const unavailableCopy = harnessUnavailableCopy(isDefault);\n\n return (\n <li className=\"flex flex-col gap-inline px-row py-row transition-colors hover:bg-background-components-hover\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n {isDefault ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"starLine\"\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>Default harness</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">Default harness</span>\n </>\n ) : null}\n <Text size=\"md\" bold className=\"truncate\">\n {harness.label}\n </Text>\n {!isAvailable ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"errorWarningLine\"\n className=\"size-16 text-foreground-warning-base\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>{unavailableCopy}</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">{unavailableCopy}</span>\n </>\n ) : null}\n </div>\n {!isDefault ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n disabled={isSettingDefault}\n aria-label={`Open ${harness.label} harness actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n icon=\"starLine\"\n disabled={isSettingDefault || !isAvailable}\n onSelect={() => {\n onSetDefault(harness);\n }}\n >\n Set as default\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n {defaultError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{defaultError}</Text>\n </Callout>\n ) : null}\n </li>\n );\n}\n\nfunction harnessUnavailableCopy(isDefault: boolean): string {\n const base = 'Configure a compatible model provider to use this harness.';\n if (!isDefault) return base;\n return `${base} The stored default harness cannot currently run in this workspace.`;\n}\n\nfunction HarnessRowsSkeleton() {\n return (\n <ul\n role=\"status\"\n aria-label=\"Loading harnesses\"\n className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}\n >\n {[0, 1].map((row) => (\n <li key={row} className=\"flex items-center gap-cluster px-row py-row\">\n <Skeleton className=\"size-16 shrink-0\" />\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"ml-auto size-28 shrink-0\" />\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","IconButton","Callout","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","Icon","Skeleton","toast","Tooltip","TooltipContent","TooltipTrigger","Header","Text","cn","useRef","useState","DEFAULT_HARNESS","listHarnesses","useModelProviderConfigsQuery","useSetDefaultHarnessMutation","modelProviderConfigErrorToFormError","isHarnessAvailable","SURFACE_CLASS","WorkspaceHarnessesSection","workspaceId","configsQuery","setDefaultHarness","activeWorkspaceIdRef","defaultRequestSeqRef","activeDefaultRequestRef","pendingDefaultHarness","setPendingDefaultHarness","defaultError","setDefaultError","configs","data","defaultHarnessId","current","isActiveDefaultRequest","request","activeRequest","id","handleSetDefault","harness","harnessId","undefined","mutateAsync","success","label","error","mapped","message","section","className","aria-label","div","variant","size","isPending","HarnessRowsSkeleton","isError","query","subject","ul","map","HarnessRow","isDefault","isAvailable","isSettingDefault","onSetDefault","unavailableCopy","harnessUnavailableCopy","li","asChild","span","name","aria-hidden","bold","icon","disabled","align","onSelect","role","type","base","row"],"mappings":";AAAA,SAAQA,cAAc,QAAO,qBAAqB;AAClD,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SACEC,YAAY,EACZC,mBAAmB,EACnBC,gBAAgB,EAChBC,mBAAmB,QACd,kCAAkC;AACzC,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAC1D,SAAQC,EAAE,QAAO,0BAA0B;AAC3C,SAAQC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AACvC,SAAQC,eAAe,EAAEC,aAAa,QAAO,0BAA0B;AAEvE,SACEC,4BAA4B,EAC5BC,4BAA4B,QACvB,gCAAgC;AACvC,SAAQC,mCAAmC,QAAO,mBAAmB;AACrE,SAAQC,kBAAkB,QAAO,4BAA4B;AAE7D,MAAMC,gBACJ;AAEF,OAAO,SAASC,0BAA0B,EAACC,WAAW,EAAwB;IAC5E,MAAMC,eAAeP,6BAA6BM;IAClD,MAAME,oBAAoBP;IAC1B,MAAMQ,uBAAuBb,OAAOU;IACpC,MAAMI,uBAAuBd,OAAO;IACpC,MAAMe,0BAA0Bf,OAAiD;IACjF,MAAM,CAACgB,uBAAuBC,yBAAyB,GAAGhB,SAGhD;IACV,MAAM,CAACiB,cAAcC,gBAAgB,GAAGlB;IAGxC,MAAMmB,UAAUT,aAAaU,IAAI,EAAED,WAAW,EAAE;IAChD,MAAME,mBAAmBX,aAAaU,IAAI,EAAEC,oBAAoBpB;IAChEW,qBAAqBU,OAAO,GAAGb;IAE/B,SAASc,uBAAuBC,OAA0C;QACxE,MAAMC,gBAAgBX,wBAAwBQ,OAAO;QACrD,OACEV,qBAAqBU,OAAO,KAAKE,QAAQf,WAAW,IACpDgB,eAAehB,gBAAgBe,QAAQf,WAAW,IAClDgB,cAAcC,EAAE,KAAKF,QAAQE,EAAE;IAEnC;IAEA,eAAeC,iBAAiBC,OAA0B;QACxD,IAAId,wBAAwBQ,OAAO,EAAEb,gBAAgBA,aAAa;QAElE,MAAMe,UAAU;YAACf;YAAaiB,IAAIb,qBAAqBS,OAAO,GAAG;QAAC;QAClET,qBAAqBS,OAAO,GAAGE,QAAQE,EAAE;QACzCZ,wBAAwBQ,OAAO,GAAGE;QAClCR,yBAAyB;YAACP;YAAaoB,WAAWD,QAAQF,EAAE;QAAA;QAC5DR,gBAAgBY;QAChB,IAAI;YACF,MAAMnB,kBAAkBoB,WAAW,CAAC;gBAClCtB;gBACAoB,WAAWD,QAAQF,EAAE;YACvB;YACA,IAAI,CAACH,uBAAuBC,UAAU;YACtChC,MAAMwC,OAAO,CAAC,GAAGJ,QAAQK,KAAK,CAAC,2BAA2B,CAAC;QAC7D,EAAE,OAAOC,OAAO;YACd,IAAI,CAACX,uBAAuBC,UAAU;YACtC,MAAMW,SAAS9B,oCAAoC6B;YACnDhB,gBAAgB;gBACdT;gBACAoB,WAAWD,QAAQF,EAAE;gBACrBU,SAASD,OAAOC,OAAO,IAAI;YAC7B;QACF,SAAU;YACR,IAAIb,uBAAuBC,UAAU;gBACnCV,wBAAwBQ,OAAO,GAAG;gBAClCN,yBAAyB;YAC3B;QACF;IACF;IAEA,qBACE,MAACqB;QAAQC,WAAU;QAA0BC,cAAW;;0BACtD,MAACC;gBAAIF,WAAU;;kCACb,KAAC1C;wBAAO6C,SAAQ;kCAAK;;kCACrB,KAAC5C;wBAAK6C,MAAK;wBAAKJ,WAAU;kCAAgC;;;;YAK3D5B,aAAaiC,SAAS,iBAAG,KAACC,2BAAyB;YAEnDlC,aAAamC,OAAO,IAAInC,aAAaU,IAAI,KAAKU,0BAC7C,KAACU;gBAAIF,WAAWxC,GAAGS,eAAe;0BAChC,cAAA,KAACxB;oBAAe+D,OAAOpC;oBAAcqC,SAAQ;;iBAE7C;YAEHrC,aAAaU,IAAI,KAAKU,0BACrB,KAACkB;gBAAGV,WAAWxC,GAAG,uCAAuCS;0BACtDL,gBAAgB+C,GAAG,CAAC,CAACrB,wBACpB,KAACsB;wBAECtB,SAASA;wBACTuB,WAAWvB,QAAQF,EAAE,KAAKL;wBAC1B+B,aAAa9C,mBAAmBsB,SAAST;wBACzCkC,kBAAkBtC,uBAAuBN,gBAAgBA;wBACzDQ,cACEA,cAAcR,gBAAgBA,eAAeQ,aAAaY,SAAS,KAAKD,QAAQF,EAAE,GAC9ET,aAAamB,OAAO,GACpBN;wBAENwB,cAAc3B;uBAVTC,QAAQF,EAAE;iBAcnB;;;AAGV;AAEA,SAASwB,WAAW,EAClBtB,OAAO,EACPuB,SAAS,EACTC,WAAW,EACXC,gBAAgB,EAChBpC,YAAY,EACZqC,YAAY,EAQb;IACC,MAAMC,kBAAkBC,uBAAuBL;IAE/C,qBACE,MAACM;QAAGnB,WAAU;;0BACZ,MAACE;gBAAIF,WAAU;;kCACb,MAACE;wBAAIF,WAAU;;4BACZa,0BACC;;kDACE,MAAC1D;;0DACC,KAACE;gDAAe+D,OAAO;0DACrB,cAAA,KAACC;oDAAKrB,WAAU;8DACd,cAAA,KAAChD;wDACCsE,MAAK;wDACLtB,WAAU;wDACVuB,aAAW;;;;0DAIjB,KAACnE;0DAAe;;;;kDAElB,KAACiE;wCAAKrB,WAAU;kDAAU;;;iCAE1B;0CACJ,KAACzC;gCAAK6C,MAAK;gCAAKoB,IAAI;gCAACxB,WAAU;0CAC5BV,QAAQK,KAAK;;4BAEf,CAACmB,4BACA;;kDACE,MAAC3D;;0DACC,KAACE;gDAAe+D,OAAO;0DACrB,cAAA,KAACC;oDAAKrB,WAAU;8DACd,cAAA,KAAChD;wDACCsE,MAAK;wDACLtB,WAAU;wDACVuB,aAAW;;;;0DAIjB,KAACnE;0DAAgB6D;;;;kDAEnB,KAACI;wCAAKrB,WAAU;kDAAWiB;;;iCAE3B;;;oBAEL,CAACJ,0BACA,MAACjE;;0CACC,KAACG;gCAAoBqE,OAAO;0CAC1B,cAAA,KAAC1E;oCACC0D,MAAK;oCACLD,SAAQ;oCACRsB,MAAK;oCACLC,UAAUX;oCACVd,cAAY,CAAC,KAAK,EAAEX,QAAQK,KAAK,CAAC,gBAAgB,CAAC;;;0CAGvD,KAAC9C;gCAAoB8E,OAAM;0CACzB,cAAA,KAAC7E;oCACC2E,MAAK;oCACLC,UAAUX,oBAAoB,CAACD;oCAC/Bc,UAAU;wCACRZ,aAAa1B;oCACf;8CACD;;;;yBAKH;;;YAELX,6BACC,KAAChC;gBAAQkF,MAAK;gBAAQC,MAAK;0BACzB,cAAA,KAACvE;oBAAK6C,MAAK;8BAAMzB;;iBAEjB;;;AAGV;AAEA,SAASuC,uBAAuBL,SAAkB;IAChD,MAAMkB,OAAO;IACb,IAAI,CAAClB,WAAW,OAAOkB;IACvB,OAAO,GAAGA,KAAK,mEAAmE,CAAC;AACrF;AAEA,SAASzB;IACP,qBACE,KAACI;QACCmB,MAAK;QACL5B,cAAW;QACXD,WAAWxC,GAAG,uCAAuCS;kBAEpD;YAAC;YAAG;SAAE,CAAC0C,GAAG,CAAC,CAACqB,oBACX,MAACb;gBAAanB,WAAU;;kCACtB,KAAC/C;wBAAS+C,WAAU;;kCACpB,KAAC/C;wBAAS+C,WAAU;;kCACpB,KAAC/C;wBAAS+C,WAAU;;;eAHbgC;;AAQjB"}
1
+ {"version":3,"sources":["../../src/components/harnesses-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Panel, PanelBody, PanelRow} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {useRef, useState} from 'react';\nimport {DEFAULT_HARNESS, listHarnesses} from '#core/harness-policy.js';\nimport type {HarnessDescriptor, HarnessId} from '#core/models.js';\nimport {\n useModelProviderConfigsQuery,\n useSetDefaultHarnessMutation,\n} from '#hooks/api/model-providers.js';\nimport {modelProviderConfigErrorToFormError} from './form-errors.js';\nimport {isHarnessAvailable} from './harness-availability.js';\n\nexport function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string}) {\n const configsQuery = useModelProviderConfigsQuery(workspaceId);\n const setDefaultHarness = useSetDefaultHarnessMutation();\n const activeWorkspaceIdRef = useRef(workspaceId);\n const defaultRequestSeqRef = useRef(0);\n const activeDefaultRequestRef = useRef<{workspaceId: string; id: number} | null>(null);\n const [pendingDefaultHarness, setPendingDefaultHarness] = useState<{\n workspaceId: string;\n harnessId: HarnessId;\n } | null>(null);\n const [defaultError, setDefaultError] = useState<\n {workspaceId: string; harnessId: HarnessId; message: string} | undefined\n >();\n const configs = configsQuery.data?.configs ?? [];\n const defaultHarnessId = configsQuery.data?.defaultHarnessId ?? DEFAULT_HARNESS;\n activeWorkspaceIdRef.current = workspaceId;\n\n function isActiveDefaultRequest(request: {workspaceId: string; id: number}) {\n const activeRequest = activeDefaultRequestRef.current;\n return (\n activeWorkspaceIdRef.current === request.workspaceId &&\n activeRequest?.workspaceId === request.workspaceId &&\n activeRequest.id === request.id\n );\n }\n\n async function handleSetDefault(harness: HarnessDescriptor) {\n if (activeDefaultRequestRef.current?.workspaceId === workspaceId) return;\n\n const request = {workspaceId, id: defaultRequestSeqRef.current + 1};\n defaultRequestSeqRef.current = request.id;\n activeDefaultRequestRef.current = request;\n setPendingDefaultHarness({workspaceId, harnessId: harness.id});\n setDefaultError(undefined);\n try {\n await setDefaultHarness.mutateAsync({\n workspaceId,\n harnessId: harness.id,\n });\n if (!isActiveDefaultRequest(request)) return;\n toast.success(`${harness.label} is now the default harness`);\n } catch (error) {\n if (!isActiveDefaultRequest(request)) return;\n const mapped = modelProviderConfigErrorToFormError(error);\n setDefaultError({\n workspaceId,\n harnessId: harness.id,\n message: mapped.message || 'Could not save default harness. Try again.',\n });\n } finally {\n if (isActiveDefaultRequest(request)) {\n activeDefaultRequestRef.current = null;\n setPendingDefaultHarness(null);\n }\n }\n }\n\n return (\n <section className=\"flex flex-col gap-group\" aria-label=\"Harnesses\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Harnesses</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Harnesses available to run agent steps in this workspace.\n </Text>\n </div>\n\n {configsQuery.isPending ? <HarnessRowsSkeleton /> : null}\n\n {configsQuery.isError && configsQuery.data === undefined ? (\n <Panel>\n <QueryLoadError query={configsQuery} subject=\"harnesses\" variant=\"panel\" />\n </Panel>\n ) : null}\n\n {configsQuery.data !== undefined ? (\n <Panel>\n <PanelBody asChild>\n <ul>\n {listHarnesses().map((harness) => (\n <HarnessRow\n key={harness.id}\n harness={harness}\n isDefault={harness.id === defaultHarnessId}\n isAvailable={isHarnessAvailable(harness, configs)}\n isSettingDefault={pendingDefaultHarness?.workspaceId === workspaceId}\n defaultError={\n defaultError?.workspaceId === workspaceId &&\n defaultError.harnessId === harness.id\n ? defaultError.message\n : undefined\n }\n onSetDefault={handleSetDefault}\n />\n ))}\n </ul>\n </PanelBody>\n </Panel>\n ) : null}\n </section>\n );\n}\n\nfunction HarnessRow({\n harness,\n isDefault,\n isAvailable,\n isSettingDefault,\n defaultError,\n onSetDefault,\n}: {\n harness: HarnessDescriptor;\n isDefault: boolean;\n isAvailable: boolean;\n isSettingDefault: boolean;\n defaultError: string | undefined;\n onSetDefault: (harness: HarnessDescriptor) => void;\n}) {\n const unavailableCopy = harnessUnavailableCopy(isDefault);\n\n return (\n <PanelRow asChild className=\"flex-col items-stretch gap-inline\">\n <li>\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n {isDefault ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"starLine\"\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>Default harness</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">Default harness</span>\n </>\n ) : null}\n <Text size=\"md\" bold className=\"truncate\">\n {harness.label}\n </Text>\n {!isAvailable ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"errorWarningLine\"\n className=\"size-16 text-foreground-warning-base\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>{unavailableCopy}</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">{unavailableCopy}</span>\n </>\n ) : null}\n </div>\n {!isDefault ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n disabled={isSettingDefault}\n aria-label={`Open ${harness.label} harness actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n icon=\"starLine\"\n disabled={isSettingDefault || !isAvailable}\n onSelect={() => {\n onSetDefault(harness);\n }}\n >\n Set as default\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n {defaultError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{defaultError}</Text>\n </Callout>\n ) : null}\n </li>\n </PanelRow>\n );\n}\n\nfunction harnessUnavailableCopy(isDefault: boolean): string {\n const base = 'Configure a compatible model provider to use this harness.';\n if (!isDefault) return base;\n return `${base} The stored default harness cannot currently run in this workspace.`;\n}\n\nfunction HarnessRowsSkeleton() {\n return (\n <Panel>\n <PanelBody asChild>\n <ul role=\"status\" aria-label=\"Loading harnesses\">\n {[0, 1].map((row) => (\n <PanelRow\n asChild\n className=\"justify-start gap-cluster hover:bg-background-neutral-base\"\n key={row}\n >\n <li>\n <Skeleton className=\"size-16 shrink-0\" />\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"ml-auto size-28 shrink-0\" />\n </li>\n </PanelRow>\n ))}\n </ul>\n </PanelBody>\n </Panel>\n );\n}\n"],"names":["QueryLoadError","IconButton","Callout","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","Icon","Panel","PanelBody","PanelRow","Skeleton","toast","Tooltip","TooltipContent","TooltipTrigger","Header","Text","useRef","useState","DEFAULT_HARNESS","listHarnesses","useModelProviderConfigsQuery","useSetDefaultHarnessMutation","modelProviderConfigErrorToFormError","isHarnessAvailable","WorkspaceHarnessesSection","workspaceId","configsQuery","setDefaultHarness","activeWorkspaceIdRef","defaultRequestSeqRef","activeDefaultRequestRef","pendingDefaultHarness","setPendingDefaultHarness","defaultError","setDefaultError","configs","data","defaultHarnessId","current","isActiveDefaultRequest","request","activeRequest","id","handleSetDefault","harness","harnessId","undefined","mutateAsync","success","label","error","mapped","message","section","className","aria-label","div","variant","size","isPending","HarnessRowsSkeleton","isError","query","subject","asChild","ul","map","HarnessRow","isDefault","isAvailable","isSettingDefault","onSetDefault","unavailableCopy","harnessUnavailableCopy","li","span","name","aria-hidden","bold","icon","disabled","align","onSelect","role","type","base","row"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function ModelProviderGridSkeleton({ label }: {
2
+ label: string;
3
+ }): import("react").JSX.Element;
4
+ //# sourceMappingURL=model-provider-grid-skeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-provider-grid-skeleton.d.ts","sourceRoot":"","sources":["../../src/components/model-provider-grid-skeleton.tsx"],"names":[],"mappings":"AAGA,wBAAgB,yBAAyB,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,+BAiBjE"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Panel, PanelBody, PanelCell, PanelGrid } from '@shipfox/react-ui/panel';
3
+ import { Skeleton } from '@shipfox/react-ui/skeleton';
4
+ export function ModelProviderGridSkeleton({ label }) {
5
+ return /*#__PURE__*/ _jsx(Panel, {
6
+ children: /*#__PURE__*/ _jsx(PanelBody, {
7
+ children: /*#__PURE__*/ _jsx(PanelGrid, {
8
+ role: "status",
9
+ "aria-busy": "true",
10
+ "aria-label": label,
11
+ children: [
12
+ 0,
13
+ 1,
14
+ 2,
15
+ 3
16
+ ].map((tile)=>/*#__PURE__*/ _jsx(PanelCell, {
17
+ children: /*#__PURE__*/ _jsxs("div", {
18
+ className: "flex items-center justify-between gap-cluster px-row py-row",
19
+ children: [
20
+ /*#__PURE__*/ _jsx(Skeleton, {
21
+ className: "h-16 w-100"
22
+ }),
23
+ /*#__PURE__*/ _jsx(Skeleton, {
24
+ className: "h-16 w-64 shrink-0"
25
+ })
26
+ ]
27
+ })
28
+ }, tile))
29
+ })
30
+ })
31
+ });
32
+ }
33
+
34
+ //# sourceMappingURL=model-provider-grid-skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/model-provider-grid-skeleton.tsx"],"sourcesContent":["import {Panel, PanelBody, PanelCell, PanelGrid} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\n\nexport function ModelProviderGridSkeleton({label}: {label: string}) {\n return (\n <Panel>\n <PanelBody>\n <PanelGrid role=\"status\" aria-busy=\"true\" aria-label={label}>\n {[0, 1, 2, 3].map((tile) => (\n <PanelCell key={tile}>\n <div className=\"flex items-center justify-between gap-cluster px-row py-row\">\n <Skeleton className=\"h-16 w-100\" />\n <Skeleton className=\"h-16 w-64 shrink-0\" />\n </div>\n </PanelCell>\n ))}\n </PanelGrid>\n </PanelBody>\n </Panel>\n );\n}\n"],"names":["Panel","PanelBody","PanelCell","PanelGrid","Skeleton","ModelProviderGridSkeleton","label","role","aria-busy","aria-label","map","tile","div","className"],"mappings":";AAAA,SAAQA,KAAK,EAAEC,SAAS,EAAEC,SAAS,EAAEC,SAAS,QAAO,0BAA0B;AAC/E,SAAQC,QAAQ,QAAO,6BAA6B;AAEpD,OAAO,SAASC,0BAA0B,EAACC,KAAK,EAAkB;IAChE,qBACE,KAACN;kBACC,cAAA,KAACC;sBACC,cAAA,KAACE;gBAAUI,MAAK;gBAASC,aAAU;gBAAOC,cAAYH;0BACnD;oBAAC;oBAAG;oBAAG;oBAAG;iBAAE,CAACI,GAAG,CAAC,CAACC,qBACjB,KAACT;kCACC,cAAA,MAACU;4BAAIC,WAAU;;8CACb,KAACT;oCAASS,WAAU;;8CACpB,KAACT;oCAASS,WAAU;;;;uBAHRF;;;;AAW5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"model-providers-section.d.ts","sourceRoot":"","sources":["../../src/components/model-providers-section.tsx"],"names":[],"mappings":"AAuEA,wBAAgB,8BAA8B,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA2TlF"}
1
+ {"version":3,"file":"model-providers-section.d.ts","sourceRoot":"","sources":["../../src/components/model-providers-section.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,8BAA8B,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BAgVlF"}