@neuronection/assistant-ui 0.56.0 → 0.58.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 +19 -0
- package/dist/chunk-3Y6QOTVA.js +270 -0
- package/dist/chunk-3Y6QOTVA.js.map +1 -0
- package/dist/{chunk-AP56DLCQ.js → chunk-FPX3AI3Z.js} +60 -10
- package/dist/chunk-FPX3AI3Z.js.map +1 -0
- package/dist/chunk-KDILZS2Z.js +3 -0
- package/dist/chunk-KDILZS2Z.js.map +1 -0
- package/dist/{chunk-KV67D3RP.js → chunk-O6BRNGXV.js} +2 -2
- package/dist/{chunk-KV67D3RP.js.map → chunk-O6BRNGXV.js.map} +1 -1
- package/dist/connection-test-row.js +1 -1
- package/dist/index.d.ts +107 -1
- package/dist/index.js +7 -6
- package/dist/model-registry.d.ts +10 -0
- package/dist/model-registry.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/web-search-providers.d.ts +99 -0
- package/dist/web-search-providers.js +3 -0
- package/dist/web-search-providers.js.map +1 -0
- package/package.json +5 -1
- package/dist/chunk-AP56DLCQ.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @neuronection/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.58.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`255c074`](https://github.com/neuronection/assistant-ui/commit/255c074c16bdbc7cc4d919d9ffea0d0334e0462e) Thanks [@constLiakos](https://github.com/constLiakos)! - `WebSearchProvidersPanel` — the family web-search settings surface
|
|
8
|
+
(ADR-0035 / plan 28): a controlled, presentational ordered multi-provider
|
|
9
|
+
list (enable, reorder, probe-test, edit, delete) with a catalog-driven
|
|
10
|
+
add/edit form. Data contracts mirror the family `providers[]` shape and
|
|
11
|
+
the vendored `search-providers.json` catalog; persistence, key sealing and
|
|
12
|
+
probe calls stay app-side. Study-assistant is the first consumer (its
|
|
13
|
+
local panel is deleted same-commit on adoption); desktop and career follow
|
|
14
|
+
in plan 28 Phases 3–4.
|
|
15
|
+
|
|
16
|
+
## 0.57.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [`087968c`](https://github.com/neuronection/assistant-ui/commit/087968cbc8b8b2dd477a897c0221ac001550d2db) Thanks [@constLiakos](https://github.com/constLiakos)! - **ModelRegistry: provider context, modern manual toggle, capability implications.** Three additive refinements to the add/edit modal. A discreet **provider context banner** (configured name + base URL, sr-only `providerContextLabel` prefix) sits at the top so the form always answers "model of _what_?". The **manual-id toggle** becomes a labelled ghost control with a pen icon, right-aligned, and swaps direction copy when manual entry is active (`useCatalogLabel`, default 'Pick from the catalog', with a library icon). New `capImplications` prop expresses **selection implications between combinable caps** — e.g. `{ vision: ['text'], tools: ['text'] }`: selecting a modifier auto-enables its text base, and deselecting the base drops everything that implies it (catalog picks normalize the same way). All optional; without them the form behaves exactly as before. Docs updated in the same change.
|
|
21
|
+
|
|
3
22
|
## 0.56.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"]}
|
|
@@ -6,7 +6,7 @@ import { Button } from './chunk-4WBBF5LH.js';
|
|
|
6
6
|
import { Spinner } from './chunk-SQHW7TYF.js';
|
|
7
7
|
import { cn } from './chunk-RVVXVTH3.js';
|
|
8
8
|
import * as React from 'react';
|
|
9
|
-
import { ChevronDown, ChevronRight, Pencil, X, Plus, Check, ChevronUp } from 'lucide-react';
|
|
9
|
+
import { ChevronDown, ChevronRight, Pencil, X, Plus, PenLine, Library, Check, ChevronUp } from 'lucide-react';
|
|
10
10
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
11
|
|
|
12
12
|
var PARAMETRIC_CAPS = /* @__PURE__ */ new Set(["text", "vision", "tools"]);
|
|
@@ -15,6 +15,22 @@ var normalizeCaps = (next, standalone) => {
|
|
|
15
15
|
const standaloneHit = next.find((cap) => standalone.has(cap));
|
|
16
16
|
return standaloneHit ? [standaloneHit] : next;
|
|
17
17
|
};
|
|
18
|
+
var applyImplications = (next, implications, toggled, selected) => {
|
|
19
|
+
if (Object.keys(implications).length === 0) return next;
|
|
20
|
+
if (selected) {
|
|
21
|
+
const implied = implications[toggled] ?? [];
|
|
22
|
+
return [...next, ...implied.filter((cap) => !next.includes(cap))];
|
|
23
|
+
}
|
|
24
|
+
const dependents = Object.entries(implications).filter(([, values]) => values.includes(toggled)).map(([cap]) => cap);
|
|
25
|
+
return dependents.length === 0 ? next : next.filter((cap) => cap !== toggled && !dependents.includes(cap));
|
|
26
|
+
};
|
|
27
|
+
var withImplications = (caps, implications) => {
|
|
28
|
+
let out = [...caps];
|
|
29
|
+
for (const cap of caps) {
|
|
30
|
+
out = applyImplications(out, implications, cap, true);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
};
|
|
18
34
|
var CUSTOM = "__custom__";
|
|
19
35
|
function NumberField({
|
|
20
36
|
label,
|
|
@@ -108,6 +124,8 @@ var ModelRegistry = React.forwardRef(
|
|
|
108
124
|
editTitle = "Edit model",
|
|
109
125
|
selectModelLabel = "Model",
|
|
110
126
|
manualIdToggleLabel = "Enter the model id manually",
|
|
127
|
+
useCatalogLabel = "Pick from the catalog",
|
|
128
|
+
providerContextLabel = "Provider",
|
|
111
129
|
editLabel = "Edit",
|
|
112
130
|
removeLabel = "Remove",
|
|
113
131
|
missingLabel = "Missing",
|
|
@@ -115,6 +133,7 @@ var ModelRegistry = React.forwardRef(
|
|
|
115
133
|
capsHint,
|
|
116
134
|
standaloneCaps,
|
|
117
135
|
standaloneLabel = "Standalone",
|
|
136
|
+
capImplications,
|
|
118
137
|
searchPlaceholder = "Search models\u2026",
|
|
119
138
|
searchLabel = "Search models",
|
|
120
139
|
emptyProviderLabel = "No models yet \u2014 use Add model to pick from the live catalog.",
|
|
@@ -210,7 +229,8 @@ var ModelRegistry = React.forwardRef(
|
|
|
210
229
|
const remote = (remoteModels ?? []).find((entry) => entry.id === remoteId);
|
|
211
230
|
setModal((current) => {
|
|
212
231
|
if (!current) return current;
|
|
213
|
-
const
|
|
232
|
+
const normalized = remote ? normalizeCaps([...remote.caps], standaloneSet) : current.caps;
|
|
233
|
+
const nextCaps = withImplications(normalized, capImplications ?? {});
|
|
214
234
|
return {
|
|
215
235
|
...current,
|
|
216
236
|
externalId: remoteId,
|
|
@@ -224,10 +244,11 @@ var ModelRegistry = React.forwardRef(
|
|
|
224
244
|
setModal((current) => {
|
|
225
245
|
if (!current) return current;
|
|
226
246
|
const isStandalone = standaloneSet.has(value);
|
|
227
|
-
const
|
|
247
|
+
const raw = isStandalone ? (
|
|
228
248
|
// a standalone kind replaces everything (or clears when toggled off)
|
|
229
249
|
current.caps.includes(value) ? current.caps.filter((entry) => entry !== value) : [value]
|
|
230
250
|
) : current.caps.includes(value) ? current.caps.filter((entry) => entry !== value) : [...current.caps.filter((entry) => !standaloneSet.has(entry)), value];
|
|
251
|
+
const nextCaps = isStandalone ? raw : applyImplications(raw, capImplications ?? {}, value, !current.caps.includes(value));
|
|
231
252
|
return {
|
|
232
253
|
...current,
|
|
233
254
|
caps: nextCaps,
|
|
@@ -305,6 +326,24 @@ var ModelRegistry = React.forwardRef(
|
|
|
305
326
|
return /* @__PURE__ */ jsx(Modal, { open: true, onOpenChange: (open) => !open ? resetModal() : void 0, children: /* @__PURE__ */ jsxs(ModalContent, { className: "max-h-[85vh] w-full max-w-lg overflow-y-auto", children: [
|
|
306
327
|
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { className: "text-base", children: modal.mode === "add" ? addTitle : `${editTitle} \u2014 ${modal.externalId}` }) }),
|
|
307
328
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 p-4", children: [
|
|
329
|
+
provider ? /* @__PURE__ */ jsxs(
|
|
330
|
+
"div",
|
|
331
|
+
{
|
|
332
|
+
"data-as": "model-registry-provider-context",
|
|
333
|
+
className: "flex min-w-0 items-center gap-2 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-muted)]/50 px-3 py-2",
|
|
334
|
+
children: [
|
|
335
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
336
|
+
providerContextLabel,
|
|
337
|
+
":"
|
|
338
|
+
] }),
|
|
339
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs font-medium text-[var(--as-fg)]", children: provider.name }),
|
|
340
|
+
provider.baseUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
341
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-[var(--as-border)]", children: "\xB7" }),
|
|
342
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-mono text-[10px] text-[var(--as-muted-fg)]", children: provider.baseUrl })
|
|
343
|
+
] }) : null
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
) : null,
|
|
308
347
|
modal.mode === "add" ? remoteState === "loading" ? /* @__PURE__ */ jsxs("p", { className: "flex items-center justify-center gap-2 py-4 text-xs text-[var(--as-muted-fg)]", children: [
|
|
309
348
|
/* @__PURE__ */ jsx(Spinner, { size: "sm" }),
|
|
310
349
|
remoteLoadingLabel
|
|
@@ -324,13 +363,17 @@ var ModelRegistry = React.forwardRef(
|
|
|
324
363
|
className: "w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 font-mono text-sm text-[var(--as-fg)]"
|
|
325
364
|
}
|
|
326
365
|
)
|
|
327
|
-
] }) : /* @__PURE__ */
|
|
366
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
328
367
|
"button",
|
|
329
368
|
{
|
|
330
369
|
type: "button",
|
|
331
|
-
|
|
370
|
+
"data-as": "model-registry-manual-toggle",
|
|
371
|
+
className: "inline-flex cursor-pointer items-center gap-1.5 self-end rounded-[var(--as-radius)] border border-[var(--as-border)] bg-transparent px-2.5 py-1 text-xs text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
332
372
|
onClick: () => setModal((c) => c ? { ...c, manual: true } : c),
|
|
333
|
-
children:
|
|
373
|
+
children: [
|
|
374
|
+
/* @__PURE__ */ jsx(PenLine, { className: "size-3.5", "aria-hidden": true }),
|
|
375
|
+
manualIdToggleLabel
|
|
376
|
+
]
|
|
334
377
|
}
|
|
335
378
|
)
|
|
336
379
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -361,9 +404,16 @@ var ModelRegistry = React.forwardRef(
|
|
|
361
404
|
"button",
|
|
362
405
|
{
|
|
363
406
|
type: "button",
|
|
364
|
-
|
|
407
|
+
"data-as": "model-registry-manual-toggle",
|
|
408
|
+
className: "inline-flex cursor-pointer items-center gap-1.5 self-end rounded-[var(--as-radius)] border border-[var(--as-border)] bg-transparent px-2.5 py-1 text-xs text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
365
409
|
onClick: () => setModal((c) => c ? { ...c, manual: !c.manual } : c),
|
|
366
|
-
children:
|
|
410
|
+
children: modal.manual ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
411
|
+
/* @__PURE__ */ jsx(Library, { className: "size-3.5", "aria-hidden": true }),
|
|
412
|
+
useCatalogLabel
|
|
413
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
414
|
+
/* @__PURE__ */ jsx(PenLine, { className: "size-3.5", "aria-hidden": true }),
|
|
415
|
+
manualIdToggleLabel
|
|
416
|
+
] })
|
|
367
417
|
}
|
|
368
418
|
)
|
|
369
419
|
] }) : /* @__PURE__ */ jsx("p", { className: "font-mono text-sm text-[var(--as-fg)]", children: modal.externalId }),
|
|
@@ -692,5 +742,5 @@ var ModelRegistry = React.forwardRef(
|
|
|
692
742
|
);
|
|
693
743
|
|
|
694
744
|
export { ModelRegistry };
|
|
695
|
-
//# sourceMappingURL=chunk-
|
|
696
|
-
//# sourceMappingURL=chunk-
|
|
745
|
+
//# sourceMappingURL=chunk-FPX3AI3Z.js.map
|
|
746
|
+
//# sourceMappingURL=chunk-FPX3AI3Z.js.map
|