@neuronection/assistant-ui 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,574 @@
1
+ import { CapabilityChips } from './chunk-G3DWN2DJ.js';
2
+ import { ModelPicker } from './chunk-WHOJXXGI.js';
3
+ import { Modal, ModalContent, ModalHeader, ModalTitle, ModalFooter } from './chunk-Y7BKBG57.js';
4
+ import { Button } from './chunk-GTXGTQ6F.js';
5
+ import { Spinner } from './chunk-INTST5VG.js';
6
+ import { cn } from './chunk-BLJWR4ZV.js';
7
+ import * as React from 'react';
8
+ import { ChevronDown, ChevronRight, Pencil, X, Plus, Check, ChevronUp } from 'lucide-react';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+
11
+ var CUSTOM = "__custom__";
12
+ var ACRONYMS = ["gpt", "nlp", "ocr", "llm", "stt", "tts", "vl", "ai"];
13
+ function beautifyModelId(externalId) {
14
+ return externalId.replace(/[-:]/g, " ").replace(/(?<!\d)\.|\.(?!\d)/g, " ").split(" ").filter(Boolean).map((word) => {
15
+ if (ACRONYMS.includes(word.toLowerCase())) return word.toUpperCase();
16
+ return word.charAt(0).toUpperCase() + word.slice(1);
17
+ }).join(" ");
18
+ }
19
+ function NumberField({
20
+ label,
21
+ value,
22
+ onChange,
23
+ onClear,
24
+ step = "1",
25
+ min,
26
+ max
27
+ }) {
28
+ const bump = (delta) => {
29
+ const current = value.trim() === "" ? 0 : Number(value);
30
+ if (!Number.isFinite(current)) return;
31
+ const next = current + delta;
32
+ onChange(String(min != null && next < Number(min) ? Number(min) : next));
33
+ };
34
+ return /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
35
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: label }),
36
+ /* @__PURE__ */ jsxs("span", { className: "relative block", children: [
37
+ /* @__PURE__ */ jsx(
38
+ "input",
39
+ {
40
+ type: "number",
41
+ step,
42
+ min,
43
+ max,
44
+ value,
45
+ onChange: (event) => onChange(event.target.value),
46
+ className: "w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 pr-16 text-sm text-[var(--as-fg)] [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
47
+ }
48
+ ),
49
+ /* @__PURE__ */ jsxs("span", { className: "absolute inset-y-0 right-1.5 flex items-center gap-1", children: [
50
+ value !== "" ? /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ type: "button",
54
+ "aria-label": `${label} clear`,
55
+ onClick: onClear,
56
+ className: "cursor-pointer text-[var(--as-muted-fg)] hover:text-[var(--as-fg)]",
57
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": true })
58
+ }
59
+ ) : null,
60
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
61
+ /* @__PURE__ */ jsx(
62
+ "button",
63
+ {
64
+ type: "button",
65
+ "aria-label": `${label} increment`,
66
+ onClick: () => bump(Number(step)),
67
+ className: "cursor-pointer leading-none text-[var(--as-muted-fg)] hover:text-[var(--as-fg)]",
68
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "size-3.5", "aria-hidden": true })
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx(
72
+ "button",
73
+ {
74
+ type: "button",
75
+ "aria-label": `${label} decrement`,
76
+ onClick: () => bump(-Number(step)),
77
+ className: "cursor-pointer leading-none text-[var(--as-muted-fg)] hover:text-[var(--as-fg)]",
78
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5", "aria-hidden": true })
79
+ }
80
+ )
81
+ ] })
82
+ ] })
83
+ ] })
84
+ ] });
85
+ }
86
+ var ModelRegistry = React.forwardRef(
87
+ function ModelRegistry2({
88
+ providers,
89
+ models,
90
+ caps,
91
+ expandedProviderId,
92
+ onExpandedProviderChange,
93
+ remoteModels,
94
+ remoteState = "ready",
95
+ remoteError,
96
+ onRetryRemote,
97
+ onAddModel,
98
+ onAddAll,
99
+ onUpdateModel,
100
+ onDeleteModel,
101
+ reasoningEffortOptions,
102
+ addLabel = "Add model",
103
+ addAllLabel = "Add all",
104
+ addTitle = "Add model",
105
+ editTitle = "Edit model",
106
+ selectModelLabel = "Model",
107
+ manualIdToggleLabel = "Enter the model id manually",
108
+ editLabel = "Edit",
109
+ removeLabel = "Remove",
110
+ missingLabel = "Missing",
111
+ capsLabel = "Capabilities",
112
+ searchPlaceholder = "Search models\u2026",
113
+ searchLabel = "Search models",
114
+ emptyProviderLabel = "No models yet \u2014 use Add model to pick from the live catalog.",
115
+ externalIdRequiredLabel = "The model id is required.",
116
+ remoteEmptyLabel = "The provider listed no models.",
117
+ remoteLoadingLabel = "Loading models\u2026",
118
+ retryLabel = "Retry",
119
+ customOptionLabel = "Custom\u2026",
120
+ temperatureLabel = "Temperature",
121
+ maxTokensLabel = "Max tokens",
122
+ labelLabel = "Display label",
123
+ reasoningEffortLabel = "Reasoning effort",
124
+ saveLabel = "Save",
125
+ cancelLabel = "Cancel",
126
+ addDraftLabel = "Add model",
127
+ providersEmptyLabel = "No providers yet.",
128
+ emptyAction,
129
+ className
130
+ }, ref) {
131
+ const [modal, setModal] = React.useState(null);
132
+ const [draftError, setDraftError] = React.useState(null);
133
+ const descriptorFor = React.useCallback(
134
+ (value) => caps.find((cap) => cap.value === value) ?? { value, label: value },
135
+ [caps]
136
+ );
137
+ const resetModal = React.useCallback(() => {
138
+ setModal(null);
139
+ setDraftError(null);
140
+ }, []);
141
+ const toggleProvider = (providerId) => {
142
+ onExpandedProviderChange(expandedProviderId === providerId ? null : providerId);
143
+ };
144
+ const openAddModal = (providerId) => {
145
+ setModal({
146
+ mode: "add",
147
+ providerId,
148
+ externalId: "",
149
+ label: "",
150
+ caps: caps[0] ? [caps[0].value] : [],
151
+ reasoningEffort: "",
152
+ reasoningCustom: false,
153
+ temperature: "",
154
+ maxTokens: "",
155
+ labelTouched: false,
156
+ manual: false
157
+ });
158
+ setDraftError(null);
159
+ };
160
+ const openEditModal = (model) => {
161
+ setModal({
162
+ mode: "edit",
163
+ providerId: model.providerId,
164
+ externalId: model.externalId,
165
+ label: model.label ?? "",
166
+ caps: [...model.caps],
167
+ labelTouched: true,
168
+ reasoningEffort: model.reasoningEffort ?? "",
169
+ reasoningCustom: model.reasoningEffort != null && model.reasoningEffort !== "" && !(reasoningEffortOptions ?? []).includes(model.reasoningEffort),
170
+ temperature: model.temperature != null ? String(model.temperature) : "",
171
+ maxTokens: model.maxTokens != null ? String(model.maxTokens) : "",
172
+ model,
173
+ manual: false
174
+ });
175
+ setDraftError(null);
176
+ };
177
+ const selectRemote = (remoteId) => {
178
+ const remote = (remoteModels ?? []).find((entry) => entry.id === remoteId);
179
+ setModal(
180
+ (current) => current ? {
181
+ ...current,
182
+ externalId: remoteId,
183
+ caps: remote ? [...remote.caps] : current.caps,
184
+ label: current.labelTouched ? current.label : beautifyModelId(remoteId)
185
+ } : current
186
+ );
187
+ };
188
+ const toggleDraftCap = (value) => {
189
+ setModal(
190
+ (current) => current ? {
191
+ ...current,
192
+ caps: current.caps.includes(value) ? current.caps.filter((entry) => entry !== value) : [...current.caps, value]
193
+ } : current
194
+ );
195
+ };
196
+ const patchField = (field, value) => {
197
+ setModal((current) => current ? { ...current, [field]: value } : current);
198
+ };
199
+ const numberOr = (raw) => {
200
+ const trimmed = raw.trim();
201
+ if (!trimmed) return null;
202
+ const parsed = Number(trimmed);
203
+ return Number.isFinite(parsed) ? parsed : null;
204
+ };
205
+ const submitModal = () => {
206
+ if (!modal) return;
207
+ const externalId = modal.externalId.trim();
208
+ if (!externalId) {
209
+ setDraftError(externalIdRequiredLabel);
210
+ return;
211
+ }
212
+ const payload = {
213
+ externalId,
214
+ label: modal.label.trim() || void 0,
215
+ caps: modal.caps,
216
+ reasoningEffort: modal.reasoningEffort.trim(),
217
+ temperature: numberOr(modal.temperature),
218
+ maxTokens: numberOr(modal.maxTokens)
219
+ };
220
+ if (modal.mode === "add") {
221
+ onAddModel(modal.providerId, payload);
222
+ } else if (modal.model) {
223
+ onUpdateModel(modal.model, payload);
224
+ }
225
+ resetModal();
226
+ };
227
+ const remoteForProvider = (provider) => {
228
+ const items = remoteModels ?? [];
229
+ if (items.length === 0) return [];
230
+ return [
231
+ {
232
+ id: provider.id,
233
+ name: provider.name,
234
+ models: items.map((entry) => ({
235
+ id: entry.id,
236
+ name: entry.id,
237
+ capabilities: entry.caps
238
+ }))
239
+ }
240
+ ];
241
+ };
242
+ const renderModal = () => {
243
+ if (!modal) return null;
244
+ const provider = providers.find((entry) => entry.id === modal.providerId);
245
+ const catalog = provider ? remoteForProvider(provider) : [];
246
+ const pendingCount = (remoteModels ?? []).filter(
247
+ (remote) => !models.some(
248
+ (model) => model.providerId === modal.providerId && model.externalId === remote.id
249
+ )
250
+ ).length;
251
+ 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: [
252
+ /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { className: "text-base", children: modal.mode === "add" ? addTitle : `${editTitle} \u2014 ${modal.externalId}` }) }),
253
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 p-4", children: [
254
+ 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: [
255
+ /* @__PURE__ */ jsx(Spinner, { size: "sm" }),
256
+ remoteLoadingLabel
257
+ ] }) : remoteState === "error" ? /* @__PURE__ */ jsxs(Fragment, { children: [
258
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
259
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-[var(--as-danger)]", children: remoteError }),
260
+ onRetryRemote ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onRetryRemote, children: retryLabel }) : null
261
+ ] }),
262
+ modal.manual ? /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
263
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: selectModelLabel }),
264
+ /* @__PURE__ */ jsx(
265
+ "input",
266
+ {
267
+ type: "text",
268
+ value: modal.externalId,
269
+ onChange: (event) => patchField("externalId", event.target.value),
270
+ 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)]"
271
+ }
272
+ )
273
+ ] }) : /* @__PURE__ */ jsx(
274
+ "button",
275
+ {
276
+ type: "button",
277
+ className: "cursor-pointer self-start text-xs text-[var(--as-muted-fg)] underline-offset-2 hover:text-[var(--as-fg)] hover:underline",
278
+ onClick: () => setModal((c) => c ? { ...c, manual: true } : c),
279
+ children: manualIdToggleLabel
280
+ }
281
+ )
282
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
283
+ modal.manual ? /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
284
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: selectModelLabel }),
285
+ /* @__PURE__ */ jsx(
286
+ "input",
287
+ {
288
+ type: "text",
289
+ value: modal.externalId,
290
+ onChange: (event) => patchField("externalId", event.target.value),
291
+ 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)]"
292
+ }
293
+ )
294
+ ] }) : /* @__PURE__ */ jsx(
295
+ ModelPicker,
296
+ {
297
+ providers: catalog,
298
+ value: modal.externalId,
299
+ onChange: (modelId) => modelId ? selectRemote(modelId) : void 0,
300
+ label: selectModelLabel,
301
+ searchPlaceholder,
302
+ searchLabel,
303
+ emptyLabel: remoteEmptyLabel
304
+ }
305
+ ),
306
+ /* @__PURE__ */ jsx(
307
+ "button",
308
+ {
309
+ type: "button",
310
+ className: "cursor-pointer self-start text-xs text-[var(--as-muted-fg)] underline-offset-2 hover:text-[var(--as-fg)] hover:underline",
311
+ onClick: () => setModal((c) => c ? { ...c, manual: !c.manual } : c),
312
+ children: manualIdToggleLabel
313
+ }
314
+ )
315
+ ] }) : /* @__PURE__ */ jsx("p", { className: "font-mono text-sm text-[var(--as-fg)]", children: modal.externalId }),
316
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
317
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: labelLabel }),
318
+ /* @__PURE__ */ jsx(
319
+ "input",
320
+ {
321
+ type: "text",
322
+ value: modal.label,
323
+ onChange: (event) => setModal(
324
+ (current) => current ? { ...current, label: event.target.value, labelTouched: true } : current
325
+ ),
326
+ className: "w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)]"
327
+ }
328
+ )
329
+ ] }),
330
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
331
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: capsLabel }),
332
+ /* @__PURE__ */ jsx(
333
+ CapabilityChips,
334
+ {
335
+ caps,
336
+ selected: modal.caps,
337
+ onToggle: toggleDraftCap,
338
+ ariaLabel: capsLabel
339
+ }
340
+ )
341
+ ] }),
342
+ reasoningEffortOptions ? /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-sm", children: [
343
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--as-fg)]", children: reasoningEffortLabel }),
344
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 sm:flex-row", children: modal.reasoningCustom ? /* @__PURE__ */ jsx(
345
+ "input",
346
+ {
347
+ type: "text",
348
+ value: modal.reasoningEffort,
349
+ placeholder: customOptionLabel,
350
+ onChange: (event) => patchField("reasoningEffort", event.target.value),
351
+ 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)] sm:flex-1"
352
+ }
353
+ ) : /* @__PURE__ */ jsxs(
354
+ "select",
355
+ {
356
+ value: modal.reasoningCustom ? CUSTOM : modal.reasoningEffort,
357
+ onChange: (event) => setModal(
358
+ (current) => current ? {
359
+ ...current,
360
+ reasoningCustom: event.target.value === CUSTOM,
361
+ reasoningEffort: event.target.value === CUSTOM ? current.reasoningEffort : event.target.value
362
+ } : current
363
+ ),
364
+ className: "w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] sm:w-40",
365
+ children: [
366
+ /* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
367
+ reasoningEffortOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option)),
368
+ /* @__PURE__ */ jsx("option", { value: CUSTOM, children: customOptionLabel })
369
+ ]
370
+ }
371
+ ) })
372
+ ] }) : null,
373
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
374
+ /* @__PURE__ */ jsx(
375
+ NumberField,
376
+ {
377
+ label: temperatureLabel,
378
+ value: modal.temperature,
379
+ onChange: (value) => patchField("temperature", value),
380
+ onClear: () => patchField("temperature", ""),
381
+ step: "0.1",
382
+ min: "0",
383
+ max: "2"
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsx(
387
+ NumberField,
388
+ {
389
+ label: maxTokensLabel,
390
+ value: modal.maxTokens,
391
+ onChange: (value) => patchField("maxTokens", value),
392
+ onClear: () => patchField("maxTokens", ""),
393
+ step: "256",
394
+ min: "1"
395
+ }
396
+ )
397
+ ] }),
398
+ draftError ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-xs text-[var(--as-danger)]", children: draftError }) : null
399
+ ] }),
400
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
401
+ modal.mode === "add" && onAddAll ? /* @__PURE__ */ jsxs(
402
+ Button,
403
+ {
404
+ variant: "outline",
405
+ size: "sm",
406
+ disabled: remoteState !== "ready" || pendingCount === 0,
407
+ onClick: () => {
408
+ onAddAll(
409
+ modal.providerId,
410
+ (remoteModels ?? []).filter(
411
+ (remote) => !models.some(
412
+ (model) => model.providerId === modal.providerId && model.externalId === remote.id
413
+ )
414
+ ).map((remote) => ({
415
+ externalId: remote.id,
416
+ caps: remote.caps,
417
+ reasoningEffort: "",
418
+ temperature: null,
419
+ maxTokens: null
420
+ }))
421
+ );
422
+ resetModal();
423
+ },
424
+ children: [
425
+ /* @__PURE__ */ jsx(Plus, { "aria-hidden": true }),
426
+ addAllLabel,
427
+ " (",
428
+ pendingCount,
429
+ ")"
430
+ ]
431
+ }
432
+ ) : null,
433
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: resetModal, children: cancelLabel }),
434
+ /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: submitModal, children: [
435
+ /* @__PURE__ */ jsx(Check, { "aria-hidden": true }),
436
+ modal.mode === "add" ? addDraftLabel : saveLabel
437
+ ] })
438
+ ] })
439
+ ] }) });
440
+ };
441
+ return /* @__PURE__ */ jsxs(
442
+ "div",
443
+ {
444
+ ref,
445
+ "data-as": "model-registry",
446
+ className: cn("flex w-full flex-col gap-4", className),
447
+ children: [
448
+ providers.map((provider) => {
449
+ const expanded = expandedProviderId === provider.id;
450
+ const providerModels = models.filter((model) => model.providerId === provider.id);
451
+ return /* @__PURE__ */ jsxs(
452
+ "div",
453
+ {
454
+ "data-as": "model-registry-provider",
455
+ className: cn(
456
+ "rounded-[var(--as-radius-lg)] border bg-[var(--as-surface)] transition-all",
457
+ expanded ? "border-[var(--as-primary)] shadow-[var(--as-shadow-1)]" : "border-[var(--as-border)]"
458
+ ),
459
+ children: [
460
+ /* @__PURE__ */ jsxs(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ "aria-expanded": expanded,
465
+ onClick: () => toggleProvider(provider.id),
466
+ className: "group flex w-full cursor-pointer items-center gap-4 p-4 text-left focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
467
+ children: [
468
+ /* @__PURE__ */ jsx(
469
+ "span",
470
+ {
471
+ className: cn(
472
+ "flex size-9 shrink-0 items-center justify-center rounded-[var(--as-radius)] transition-colors",
473
+ expanded ? "bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "bg-[var(--as-muted)] text-[var(--as-muted-fg)] group-hover:bg-[var(--as-primary)]/10 group-hover:text-[var(--as-primary)]"
474
+ ),
475
+ children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-5", "aria-hidden": true }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-5", "aria-hidden": true })
476
+ }
477
+ ),
478
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
479
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2 text-sm font-semibold text-[var(--as-fg)]", children: [
480
+ provider.name,
481
+ provider.type ? /* @__PURE__ */ jsx("span", { className: "rounded-full border border-[var(--as-border)] px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-[var(--as-muted-fg)]", children: provider.type }) : null,
482
+ providerModels.length ? /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-[var(--as-muted)] px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-widest text-[var(--as-muted-fg)]", children: [
483
+ providerModels.filter((model) => model.enabled).length,
484
+ "/",
485
+ providerModels.length
486
+ ] }) : null
487
+ ] }),
488
+ provider.baseUrl ? /* @__PURE__ */ jsx("span", { className: "block truncate font-mono text-xs text-[var(--as-muted-fg)]", children: provider.baseUrl }) : null
489
+ ] })
490
+ ]
491
+ }
492
+ ),
493
+ expanded ? /* @__PURE__ */ jsxs("div", { className: "as-anim-fade flex flex-col gap-2 border-t border-[var(--as-border)] p-4", children: [
494
+ providerModels.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--as-muted-fg)]", children: emptyProviderLabel }) : null,
495
+ providerModels.map((model) => {
496
+ return /* @__PURE__ */ jsxs(
497
+ "div",
498
+ {
499
+ className: cn(
500
+ "flex min-w-0 items-center gap-2 rounded-[var(--as-radius-sm)] border border-[var(--as-border)] px-3 py-2 text-sm",
501
+ model.missing && "opacity-50"
502
+ ),
503
+ children: [
504
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-xs text-[var(--as-fg)]", children: model.externalId }),
505
+ model.missing ? /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--as-warning)]", children: missingLabel }) : null,
506
+ /* @__PURE__ */ jsx(
507
+ CapabilityChips,
508
+ {
509
+ variant: "badge",
510
+ caps: model.caps.map(descriptorFor),
511
+ selected: model.caps
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsxs(
515
+ Button,
516
+ {
517
+ variant: "outline",
518
+ size: "sm",
519
+ "aria-label": `${editLabel} ${model.externalId}`,
520
+ onClick: () => openEditModal(model),
521
+ children: [
522
+ /* @__PURE__ */ jsx(Pencil, { "aria-hidden": true }),
523
+ editLabel
524
+ ]
525
+ }
526
+ ),
527
+ /* @__PURE__ */ jsx(
528
+ "button",
529
+ {
530
+ type: "button",
531
+ "aria-label": `${removeLabel} ${model.externalId}`,
532
+ onClick: () => onDeleteModel(model),
533
+ className: "cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 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-1 focus-visible:outline-[var(--as-focus-ring)]",
534
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": true })
535
+ }
536
+ )
537
+ ]
538
+ },
539
+ model.id
540
+ );
541
+ }),
542
+ /* @__PURE__ */ jsx("div", { className: "pt-1", children: /* @__PURE__ */ jsxs(
543
+ Button,
544
+ {
545
+ variant: "outline",
546
+ size: "sm",
547
+ "aria-expanded": modal?.mode === "add" && modal.providerId === provider.id,
548
+ onClick: () => openAddModal(provider.id),
549
+ children: [
550
+ /* @__PURE__ */ jsx(Plus, { "aria-hidden": true }),
551
+ addLabel
552
+ ]
553
+ }
554
+ ) })
555
+ ] }) : null
556
+ ]
557
+ },
558
+ provider.id
559
+ );
560
+ }),
561
+ providers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 py-8 text-center", children: [
562
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--as-muted-fg)]", children: providersEmptyLabel }),
563
+ emptyAction
564
+ ] }) : null,
565
+ renderModal()
566
+ ]
567
+ }
568
+ );
569
+ }
570
+ );
571
+
572
+ export { ModelRegistry };
573
+ //# sourceMappingURL=chunk-QIXRJMCL.js.map
574
+ //# sourceMappingURL=chunk-QIXRJMCL.js.map