@neuronection/assistant-ui 0.57.0 → 0.59.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.59.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`2bf4216`](https://github.com/neuronection/assistant-ui/commit/2bf4216c2b1ea1da3f2dc1367298ce6fcd16c058) Thanks [@constLiakos](https://github.com/constLiakos)! - ModelPicker labeled null option (`nullOption?: { label: string }`): the option list leads with the labeled null (inherit) entry — rendered "Inherits — {resolved default model}" by the app — and choosing it assigns `null` (plan 29 / ADR-0036, the Decision defaults card's inherit rows). The props are discriminated: with `nullOption` set, `onChange` receives `string | null`; without it the contract is unchanged.
8
+
9
+ ## 0.58.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`255c074`](https://github.com/neuronection/assistant-ui/commit/255c074c16bdbc7cc4d919d9ffea0d0334e0462e) Thanks [@constLiakos](https://github.com/constLiakos)! - `WebSearchProvidersPanel` — the family web-search settings surface
14
+ (ADR-0035 / plan 28): a controlled, presentational ordered multi-provider
15
+ list (enable, reorder, probe-test, edit, delete) with a catalog-driven
16
+ add/edit form. Data contracts mirror the family `providers[]` shape and
17
+ the vendored `search-providers.json` catalog; persistence, key sealing and
18
+ probe calls stay app-side. Study-assistant is the first consumer (its
19
+ local panel is deleted same-commit on adoption); desktop and career follow
20
+ in plan 28 Phases 3–4.
21
+
3
22
  ## 0.57.0
4
23
 
5
24
  ### Minor Changes
@@ -0,0 +1,270 @@
1
+ import { ConnectionTestRow } from './chunk-O6BRNGXV.js';
2
+ import { Button } from './chunk-4WBBF5LH.js';
3
+ import { cn } from './chunk-RVVXVTH3.js';
4
+ import * as React from 'react';
5
+ import { Plus, ChevronUp, ChevronDown, Pencil, Trash2 } from 'lucide-react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var inputClass = "min-w-0 flex-1 rounded-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]";
9
+ var WebSearchProvidersPanel = React.forwardRef(function WebSearchProvidersPanel2({
10
+ providers,
11
+ catalog,
12
+ probes = {},
13
+ testingId = null,
14
+ busy = false,
15
+ labels,
16
+ onSave,
17
+ onDelete,
18
+ onMove,
19
+ onToggle,
20
+ onTest,
21
+ className
22
+ }, ref) {
23
+ const t = React.useMemo(
24
+ () => ({
25
+ title: labels?.title ?? "Web search providers",
26
+ hint: labels?.hint,
27
+ addLabel: labels?.addLabel ?? "Add provider",
28
+ emptyLabel: labels?.emptyLabel ?? "No web search providers yet.",
29
+ nameLabel: labels?.nameLabel ?? "Name (optional)",
30
+ typeLabel: labels?.typeLabel ?? "Provider type",
31
+ baseUrlLabel: labels?.baseUrlLabel ?? "Base URL (optional)",
32
+ baseUrlRequiredLabel: labels?.baseUrlRequiredLabel ?? "Base URL (required)",
33
+ keyLabel: labels?.keyLabel ?? "API key",
34
+ keyOptionalLabel: labels?.keyOptionalLabel ?? "API key (optional)",
35
+ keyStoredLabel: labels?.keyStoredLabel ?? "API key saved",
36
+ noKeyLabel: labels?.noKeyLabel ?? "no key",
37
+ saveLabel: labels?.saveLabel ?? "Save provider",
38
+ cancelLabel: labels?.cancelLabel ?? "Cancel",
39
+ editLabel: labels?.editLabel ?? "Edit provider",
40
+ deleteLabel: labels?.deleteLabel ?? "Delete provider",
41
+ moveUpLabel: labels?.moveUpLabel ?? "Move up",
42
+ moveDownLabel: labels?.moveDownLabel ?? "Move down",
43
+ enabledAriaLabel: labels?.enabledAriaLabel ?? "Enable provider",
44
+ disabledBadge: labels?.disabledBadge ?? "disabled",
45
+ testLabel: labels?.testLabel ?? "Test",
46
+ probeLabels: { ...labels?.probeLabels }
47
+ }),
48
+ [labels]
49
+ );
50
+ const [draft, setDraft] = React.useState(null);
51
+ const openAdd = () => {
52
+ const first = catalog.order[0];
53
+ if (first) setDraft({ type: first, name: "", baseUrl: "", googleCx: "", apiKey: null });
54
+ };
55
+ const openEdit = (provider) => setDraft({
56
+ id: provider.id,
57
+ type: provider.type,
58
+ name: provider.name,
59
+ baseUrl: provider.baseUrl ?? "",
60
+ googleCx: provider.googleCx ?? "",
61
+ apiKey: null
62
+ });
63
+ const draftEntry = draft ? catalog.entries[draft.type] : void 0;
64
+ const draftCxField = draftEntry?.fields?.some((field) => field.id === "google_cx") ?? false;
65
+ const saveDisabled = busy || draftEntry?.baseUrlRequired && !(draft?.baseUrl ?? "").trim() || draft?.id == null && (draftEntry?.requiresKey ?? false) && !(draft?.apiKey ?? "").trim();
66
+ return /* @__PURE__ */ jsxs(
67
+ "div",
68
+ {
69
+ ref,
70
+ "data-as": "web-search-providers",
71
+ className: cn(
72
+ "rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-4",
73
+ className
74
+ ),
75
+ children: [
76
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
77
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
78
+ /* @__PURE__ */ jsx("p", { className: "text-base font-medium text-[var(--as-fg)]", children: t.title }),
79
+ t.hint ? /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-[var(--as-muted-fg)]", children: t.hint }) : null
80
+ ] }),
81
+ draft === null ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: openAdd, children: [
82
+ /* @__PURE__ */ jsx(Plus, { className: "size-4", "aria-hidden": true }),
83
+ t.addLabel
84
+ ] }) : null
85
+ ] }),
86
+ /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-2", "data-testid": "wsp-rows", children: providers.map((provider, index) => {
87
+ const probe = probes[provider.id];
88
+ const isTesting = testingId === provider.id;
89
+ const showProbeRow = probe != null || isTesting;
90
+ const probeStatus = !showProbeRow ? null : isTesting ? "testing" : probe.status === "ok" ? "ok" : "fail";
91
+ const probeText = probe && probe.status !== "ok" && probe.error ? probe.error : probe ? t.probeLabels[probe.status] ?? probe.status : null;
92
+ return /* @__PURE__ */ jsxs(
93
+ "li",
94
+ {
95
+ "data-testid": `wsp-row-${provider.id}`,
96
+ className: "border-border border-[var(--as-border)] space-y-1 rounded-[var(--as-radius-sm)] border p-3",
97
+ children: [
98
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
99
+ /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm font-medium text-[var(--as-fg)]", children: [
100
+ /* @__PURE__ */ jsx(
101
+ "input",
102
+ {
103
+ type: "checkbox",
104
+ checked: provider.enabled,
105
+ "aria-label": `${t.enabledAriaLabel}: ${provider.name}`,
106
+ onChange: (event) => onToggle(provider.id, event.target.checked)
107
+ }
108
+ ),
109
+ provider.name
110
+ ] }),
111
+ /* @__PURE__ */ jsx("span", { className: "rounded-full bg-[var(--as-secondary)] px-2 py-0.5 text-[11px] text-[var(--as-muted-fg)]", children: catalog.entries[provider.type]?.label ?? provider.type }),
112
+ provider.baseUrl ? /* @__PURE__ */ jsx("span", { className: "truncate font-mono text-xs text-[var(--as-muted-fg)]", children: provider.baseUrl }) : null,
113
+ /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-[var(--as-muted-fg)]", children: provider.keySet ? provider.keyHint ?? t.keyStoredLabel : t.noKeyLabel })
114
+ ] }),
115
+ showProbeRow ? /* @__PURE__ */ jsx(
116
+ ConnectionTestRow,
117
+ {
118
+ variant: "inline",
119
+ status: probeStatus ?? "idle",
120
+ latencyMs: probe?.latencyMs ?? null,
121
+ label: catalog.entries[provider.type]?.label ?? provider.type,
122
+ testLabel: t.testLabel,
123
+ okLabel: t.probeLabels.ok ?? probe?.status ?? "ok",
124
+ failLabel: probeText ?? t.probeLabels.error ?? probe?.status ?? "failed",
125
+ errorMessage: probe && probe.status === "ok" ? null : probe?.error ?? null
126
+ }
127
+ ) : null,
128
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
129
+ /* @__PURE__ */ jsx(
130
+ Button,
131
+ {
132
+ variant: "outline",
133
+ size: "sm",
134
+ disabled: busy || testingId === provider.id,
135
+ onClick: () => onTest(provider.id),
136
+ children: t.testLabel
137
+ }
138
+ ),
139
+ /* @__PURE__ */ jsx(
140
+ Button,
141
+ {
142
+ variant: "ghost",
143
+ size: "icon",
144
+ "aria-label": `${t.moveUpLabel}: ${provider.name}`,
145
+ disabled: index === 0 || busy,
146
+ onClick: () => onMove(provider.id, "up"),
147
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "size-4", "aria-hidden": true })
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsx(
151
+ Button,
152
+ {
153
+ variant: "ghost",
154
+ size: "icon",
155
+ "aria-label": `${t.moveDownLabel}: ${provider.name}`,
156
+ disabled: index === providers.length - 1 || busy,
157
+ onClick: () => onMove(provider.id, "down"),
158
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4", "aria-hidden": true })
159
+ }
160
+ ),
161
+ /* @__PURE__ */ jsx(
162
+ Button,
163
+ {
164
+ variant: "ghost",
165
+ size: "icon",
166
+ "aria-label": `${t.editLabel}: ${provider.name}`,
167
+ disabled: busy,
168
+ onClick: () => openEdit(provider),
169
+ children: /* @__PURE__ */ jsx(Pencil, { className: "size-4", "aria-hidden": true })
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsx(
173
+ Button,
174
+ {
175
+ variant: "ghost",
176
+ size: "icon",
177
+ "aria-label": `${t.deleteLabel}: ${provider.name}`,
178
+ disabled: busy,
179
+ onClick: () => onDelete(provider.id),
180
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true })
181
+ }
182
+ ),
183
+ !provider.enabled ? /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--as-muted-fg)]", children: t.disabledBadge }) : null
184
+ ] })
185
+ ]
186
+ },
187
+ provider.id
188
+ );
189
+ }) }),
190
+ providers.length === 0 && draft === null ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-[var(--as-muted-fg)]", "data-testid": "wsp-empty", children: t.emptyLabel }) : null,
191
+ draft !== null ? /* @__PURE__ */ jsxs(
192
+ "form",
193
+ {
194
+ "data-testid": "wsp-form",
195
+ className: "mt-3 space-y-2 rounded-[var(--as-radius-sm)] border border-[var(--as-border)] p-3",
196
+ onSubmit: (event) => {
197
+ event.preventDefault();
198
+ onSave(draft);
199
+ },
200
+ children: [
201
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
202
+ /* @__PURE__ */ jsx(
203
+ "select",
204
+ {
205
+ className: inputClass,
206
+ value: draft.type,
207
+ disabled: draft.id != null || busy,
208
+ "aria-label": t.typeLabel,
209
+ onChange: (event) => setDraft({ ...draft, type: event.target.value, googleCx: "" }),
210
+ children: catalog.order.map((type) => /* @__PURE__ */ jsx("option", { value: type, children: catalog.entries[type]?.label ?? type }, type))
211
+ }
212
+ ),
213
+ /* @__PURE__ */ jsx(
214
+ "input",
215
+ {
216
+ className: inputClass,
217
+ placeholder: t.nameLabel,
218
+ value: draft.name ?? "",
219
+ onChange: (event) => setDraft({ ...draft, name: event.target.value })
220
+ }
221
+ )
222
+ ] }),
223
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
224
+ /* @__PURE__ */ jsx(
225
+ "input",
226
+ {
227
+ className: inputClass,
228
+ placeholder: draftEntry?.baseUrlRequired ? t.baseUrlRequiredLabel : draftEntry?.baseUrlHint ?? t.baseUrlLabel,
229
+ value: draft.baseUrl ?? "",
230
+ onChange: (event) => setDraft({ ...draft, baseUrl: event.target.value })
231
+ }
232
+ ),
233
+ draftCxField ? /* @__PURE__ */ jsx(
234
+ "input",
235
+ {
236
+ className: cn(inputClass, "w-48 flex-none"),
237
+ placeholder: draftEntry?.fields?.find((f) => f.id === "google_cx")?.placeholder ?? "",
238
+ "aria-label": draftEntry?.fields?.find((f) => f.id === "google_cx")?.label ?? "Engine id",
239
+ value: draft.googleCx ?? "",
240
+ onChange: (event) => setDraft({ ...draft, googleCx: event.target.value })
241
+ }
242
+ ) : null,
243
+ /* @__PURE__ */ jsx(
244
+ "input",
245
+ {
246
+ type: "password",
247
+ className: cn(inputClass, "w-48 flex-none"),
248
+ placeholder: draft.id != null ? t.keyStoredLabel : draftEntry?.requiresKey ? t.keyLabel : t.keyOptionalLabel,
249
+ "aria-label": t.keyLabel,
250
+ value: draft.apiKey ?? "",
251
+ onChange: (event) => setDraft({ ...draft, apiKey: event.target.value })
252
+ }
253
+ )
254
+ ] }),
255
+ draftEntry?.notes ? /* @__PURE__ */ jsx("p", { className: "text-[11px] text-[var(--as-muted-fg)]", children: draftEntry.notes }) : null,
256
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
257
+ /* @__PURE__ */ jsx(Button, { size: "sm", type: "submit", disabled: saveDisabled, children: t.saveLabel }),
258
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", type: "button", onClick: () => setDraft(null), children: t.cancelLabel })
259
+ ] })
260
+ ]
261
+ }
262
+ ) : null
263
+ ]
264
+ }
265
+ );
266
+ });
267
+
268
+ export { WebSearchProvidersPanel };
269
+ //# sourceMappingURL=chunk-3Y6QOTVA.js.map
270
+ //# sourceMappingURL=chunk-3Y6QOTVA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/web-search-providers/WebSearchProvidersPanel.tsx"],"names":["WebSearchProvidersPanel"],"mappings":"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","file":"chunk-3Y6QOTVA.js","sourcesContent":["import * as React from 'react'\nimport { ChevronDown, ChevronUp, Pencil, Plus, Trash2 } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\nimport { ConnectionTestRow, type ConnectionTestStatus } from '../connection-test-row/ConnectionTestRow'\n\nexport type WebSearchProbeStatus =\n | 'ok'\n | 'unreachable'\n | 'auth'\n | 'rate_limit'\n | 'json_disabled'\n | 'error'\n\nexport interface WebSearchProviderView {\n id: string\n name: string\n type: string\n enabled: boolean\n baseUrl?: string | null\n googleCx?: string | null\n keyHint?: string | null\n keySet: boolean\n}\n\nexport interface WebSearchProviderCatalogField {\n id: string\n label: string\n required: boolean\n placeholder?: string\n}\n\nexport interface WebSearchProviderCatalogEntry {\n label: string\n requiresKey: boolean\n keyUrl?: string | null\n baseUrl?: string | null\n baseUrlRequired: boolean\n baseUrlHint?: string | null\n notes?: string | null\n fields?: WebSearchProviderCatalogField[]\n}\n\nexport interface WebSearchProviderCatalog {\n /** Rendering + form order for provider types. */\n order: string[]\n entries: Record<string, WebSearchProviderCatalogEntry>\n}\n\nexport interface WebSearchProviderSaveInput {\n /** Absent = create. */\n id?: string\n type: string\n name?: string | null\n baseUrl?: string | null\n googleCx?: string | null\n /** `null` keeps the stored key, a value stores it. */\n apiKey?: string | null\n}\n\nexport interface WebSearchProviderProbeView {\n status: WebSearchProbeStatus\n latencyMs?: number | null\n error?: string | null\n}\n\nexport interface WebSearchProvidersPanelLabels {\n title?: string\n hint?: string\n addLabel?: string\n emptyLabel?: string\n nameLabel?: string\n typeLabel?: string\n baseUrlLabel?: string\n baseUrlRequiredLabel?: string\n keyLabel?: string\n keyOptionalLabel?: string\n keyStoredLabel?: string\n noKeyLabel?: string\n saveLabel?: string\n cancelLabel?: string\n editLabel?: string\n deleteLabel?: string\n moveUpLabel?: string\n moveDownLabel?: string\n enabledAriaLabel?: string\n disabledBadge?: string\n testLabel?: string\n probeLabels?: Partial<Record<WebSearchProbeStatus, string>>\n}\n\nexport interface WebSearchProvidersPanelProps {\n providers: WebSearchProviderView[]\n catalog: WebSearchProviderCatalog\n /** Connection probes keyed by provider id. */\n probes?: Record<string, WebSearchProviderProbeView>\n /** Id currently mid-test (shows the testing spinner). */\n testingId?: string | null\n busy?: boolean\n labels?: WebSearchProvidersPanelLabels\n onSave: (input: WebSearchProviderSaveInput) => void\n onDelete: (id: string) => void\n onMove: (id: string, direction: 'up' | 'down') => void\n onToggle: (id: string, enabled: boolean) => void\n onTest: (id: string) => void\n className?: string\n}\n\nconst inputClass =\n 'min-w-0 flex-1 rounded-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n/**\n * Presentational ordered web-search provider list (ADR-0035 family shape):\n * enable/reorder/test/edit/delete rows plus a catalog-driven add/edit form.\n * Fully controlled — persistence, keys and probes are app-side; the panel\n * renders and emits. The first enabled provider that answers wins is the\n * family's fallback semantics, surfaced here by row order.\n */\nexport const WebSearchProvidersPanel = React.forwardRef<\n HTMLDivElement,\n WebSearchProvidersPanelProps\n>(function WebSearchProvidersPanel(\n {\n providers,\n catalog,\n probes = {},\n testingId = null,\n busy = false,\n labels,\n onSave,\n onDelete,\n onMove,\n onToggle,\n onTest,\n className,\n },\n ref,\n) {\n const t = React.useMemo(\n () => ({\n title: labels?.title ?? 'Web search providers',\n hint: labels?.hint,\n addLabel: labels?.addLabel ?? 'Add provider',\n emptyLabel: labels?.emptyLabel ?? 'No web search providers yet.',\n nameLabel: labels?.nameLabel ?? 'Name (optional)',\n typeLabel: labels?.typeLabel ?? 'Provider type',\n baseUrlLabel: labels?.baseUrlLabel ?? 'Base URL (optional)',\n baseUrlRequiredLabel: labels?.baseUrlRequiredLabel ?? 'Base URL (required)',\n keyLabel: labels?.keyLabel ?? 'API key',\n keyOptionalLabel: labels?.keyOptionalLabel ?? 'API key (optional)',\n keyStoredLabel: labels?.keyStoredLabel ?? 'API key saved',\n noKeyLabel: labels?.noKeyLabel ?? 'no key',\n saveLabel: labels?.saveLabel ?? 'Save provider',\n cancelLabel: labels?.cancelLabel ?? 'Cancel',\n editLabel: labels?.editLabel ?? 'Edit provider',\n deleteLabel: labels?.deleteLabel ?? 'Delete provider',\n moveUpLabel: labels?.moveUpLabel ?? 'Move up',\n moveDownLabel: labels?.moveDownLabel ?? 'Move down',\n enabledAriaLabel: labels?.enabledAriaLabel ?? 'Enable provider',\n disabledBadge: labels?.disabledBadge ?? 'disabled',\n testLabel: labels?.testLabel ?? 'Test',\n probeLabels: { ...labels?.probeLabels },\n }),\n [labels],\n )\n\n const [draft, setDraft] = React.useState<WebSearchProviderSaveInput | null>(null)\n\n const openAdd = () => {\n const first = catalog.order[0]\n if (first) setDraft({ type: first, name: '', baseUrl: '', googleCx: '', apiKey: null })\n }\n\n const openEdit = (provider: WebSearchProviderView) =>\n setDraft({\n id: provider.id,\n type: provider.type,\n name: provider.name,\n baseUrl: provider.baseUrl ?? '',\n googleCx: provider.googleCx ?? '',\n apiKey: null,\n })\n\n const draftEntry = draft ? catalog.entries[draft.type] : undefined\n const draftCxField = draftEntry?.fields?.some((field) => field.id === 'google_cx') ?? false\n const saveDisabled =\n busy ||\n (draftEntry?.baseUrlRequired && !(draft?.baseUrl ?? '').trim()) ||\n (draft?.id == null && (draftEntry?.requiresKey ?? false) && !(draft?.apiKey ?? '').trim())\n\n return (\n <div\n ref={ref}\n data-as=\"web-search-providers\"\n className={cn(\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-4',\n className,\n )}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-base font-medium text-[var(--as-fg)]\">{t.title}</p>\n {t.hint ? (\n <p className=\"mt-0.5 text-xs text-[var(--as-muted-fg)]\">{t.hint}</p>\n ) : null}\n </div>\n {draft === null ? (\n <Button variant=\"outline\" size=\"sm\" onClick={openAdd}>\n <Plus className=\"size-4\" aria-hidden />\n {t.addLabel}\n </Button>\n ) : null}\n </div>\n\n <ul className=\"mt-3 space-y-2\" data-testid=\"wsp-rows\">\n {providers.map((provider, index) => {\n const probe = probes[provider.id]\n const isTesting = testingId === provider.id\n const showProbeRow = probe != null || isTesting\n const probeStatus: ConnectionTestStatus | null = !showProbeRow\n ? null\n : isTesting\n ? 'testing'\n : probe!.status === 'ok'\n ? 'ok'\n : 'fail'\n const probeText =\n probe && probe.status !== 'ok' && probe.error\n ? probe.error\n : probe\n ? (t.probeLabels[probe.status] ?? probe.status)\n : null\n return (\n <li\n key={provider.id}\n data-testid={`wsp-row-${provider.id}`}\n className=\"border-border border-[var(--as-border)] space-y-1 rounded-[var(--as-radius-sm)] border p-3\"\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <label className=\"flex cursor-pointer items-center gap-2 text-sm font-medium text-[var(--as-fg)]\">\n <input\n type=\"checkbox\"\n checked={provider.enabled}\n aria-label={`${t.enabledAriaLabel}: ${provider.name}`}\n onChange={(event) => onToggle(provider.id, event.target.checked)}\n />\n {provider.name}\n </label>\n <span className=\"rounded-full bg-[var(--as-secondary)] px-2 py-0.5 text-[11px] text-[var(--as-muted-fg)]\">\n {catalog.entries[provider.type]?.label ?? provider.type}\n </span>\n {provider.baseUrl ? (\n <span className=\"truncate font-mono text-xs text-[var(--as-muted-fg)]\">\n {provider.baseUrl}\n </span>\n ) : null}\n <span className=\"ml-auto text-xs text-[var(--as-muted-fg)]\">\n {provider.keySet\n ? (provider.keyHint ?? t.keyStoredLabel)\n : t.noKeyLabel}\n </span>\n </div>\n {showProbeRow ? (\n <ConnectionTestRow\n variant=\"inline\"\n status={probeStatus ?? 'idle'}\n latencyMs={probe?.latencyMs ?? null}\n label={catalog.entries[provider.type]?.label ?? provider.type}\n testLabel={t.testLabel}\n okLabel={t.probeLabels.ok ?? probe?.status ?? 'ok'}\n failLabel={probeText ?? t.probeLabels.error ?? probe?.status ?? 'failed'}\n errorMessage={probe && probe.status === 'ok' ? null : (probe?.error ?? null)}\n />\n ) : null}\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy || testingId === provider.id}\n onClick={() => onTest(provider.id)}\n >\n {t.testLabel}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`${t.moveUpLabel}: ${provider.name}`}\n disabled={index === 0 || busy}\n onClick={() => onMove(provider.id, 'up')}\n >\n <ChevronUp className=\"size-4\" aria-hidden />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`${t.moveDownLabel}: ${provider.name}`}\n disabled={index === providers.length - 1 || busy}\n onClick={() => onMove(provider.id, 'down')}\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`${t.editLabel}: ${provider.name}`}\n disabled={busy}\n onClick={() => openEdit(provider)}\n >\n <Pencil className=\"size-4\" aria-hidden />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`${t.deleteLabel}: ${provider.name}`}\n disabled={busy}\n onClick={() => onDelete(provider.id)}\n >\n <Trash2 className=\"size-4\" aria-hidden />\n </Button>\n {!provider.enabled ? (\n <span className=\"text-xs text-[var(--as-muted-fg)]\">\n {t.disabledBadge}\n </span>\n ) : null}\n </div>\n </li>\n )\n })}\n </ul>\n\n {providers.length === 0 && draft === null ? (\n <p className=\"py-4 text-center text-sm text-[var(--as-muted-fg)]\" data-testid=\"wsp-empty\">\n {t.emptyLabel}\n </p>\n ) : null}\n\n {draft !== null ? (\n <form\n data-testid=\"wsp-form\"\n className=\"mt-3 space-y-2 rounded-[var(--as-radius-sm)] border border-[var(--as-border)] p-3\"\n onSubmit={(event) => {\n event.preventDefault()\n onSave(draft)\n }}\n >\n <div className=\"flex flex-wrap gap-2\">\n <select\n className={inputClass}\n value={draft.type}\n disabled={draft.id != null || busy}\n aria-label={t.typeLabel}\n onChange={(event) =>\n setDraft({ ...draft, type: event.target.value, googleCx: '' })\n }\n >\n {catalog.order.map((type) => (\n <option key={type} value={type}>\n {catalog.entries[type]?.label ?? type}\n </option>\n ))}\n </select>\n <input\n className={inputClass}\n placeholder={t.nameLabel}\n value={draft.name ?? ''}\n onChange={(event) => setDraft({ ...draft, name: event.target.value })}\n />\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <input\n className={inputClass}\n placeholder={\n draftEntry?.baseUrlRequired\n ? t.baseUrlRequiredLabel\n : (draftEntry?.baseUrlHint ?? t.baseUrlLabel)\n }\n value={draft.baseUrl ?? ''}\n onChange={(event) => setDraft({ ...draft, baseUrl: event.target.value })}\n />\n {draftCxField ? (\n <input\n className={cn(inputClass, 'w-48 flex-none')}\n placeholder={draftEntry?.fields?.find((f) => f.id === 'google_cx')?.placeholder ?? ''}\n aria-label={draftEntry?.fields?.find((f) => f.id === 'google_cx')?.label ?? 'Engine id'}\n value={draft.googleCx ?? ''}\n onChange={(event) => setDraft({ ...draft, googleCx: event.target.value })}\n />\n ) : null}\n <input\n type=\"password\"\n className={cn(inputClass, 'w-48 flex-none')}\n placeholder={\n draft.id != null\n ? t.keyStoredLabel\n : draftEntry?.requiresKey\n ? t.keyLabel\n : t.keyOptionalLabel\n }\n aria-label={t.keyLabel}\n value={draft.apiKey ?? ''}\n onChange={(event) => setDraft({ ...draft, apiKey: event.target.value })}\n />\n </div>\n {draftEntry?.notes ? (\n <p className=\"text-[11px] text-[var(--as-muted-fg)]\">{draftEntry.notes}</p>\n ) : null}\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" type=\"submit\" disabled={saveDisabled}>\n {t.saveLabel}\n </Button>\n <Button size=\"sm\" variant=\"ghost\" type=\"button\" onClick={() => setDraft(null)}>\n {t.cancelLabel}\n </Button>\n </div>\n </form>\n ) : null}\n </div>\n )\n})\n"]}
@@ -1,5 +1,5 @@
1
1
  import { CapabilityChips } from './chunk-H5KOC7Z5.js';
2
- import { ModelPicker } from './chunk-CCFVDJHU.js';
2
+ import { ModelPicker } from './chunk-X6L5T65Q.js';
3
3
  import { Modal, ModalContent, ModalHeader, ModalTitle, ModalFooter } from './chunk-FVQWIDB5.js';
4
4
  import { beautifyId } from './chunk-B2G66OSQ.js';
5
5
  import { Button } from './chunk-4WBBF5LH.js';
@@ -742,5 +742,5 @@ var ModelRegistry = React.forwardRef(
742
742
  );
743
743
 
744
744
  export { ModelRegistry };
745
- //# sourceMappingURL=chunk-FPX3AI3Z.js.map
746
- //# sourceMappingURL=chunk-FPX3AI3Z.js.map
745
+ //# sourceMappingURL=chunk-GR6CSKQY.js.map
746
+ //# sourceMappingURL=chunk-GR6CSKQY.js.map