@ai-matrx/agents 0.59.194 → 0.59.196

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.
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
  // settings/react/index.ts
5
- import { useSyncExternalStore } from "react";
5
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
6
6
 
7
7
  // generated/llm-enums.ts
8
8
  var LLM_PARAMS_KEYS = [
@@ -81,6 +81,168 @@ var LLM_PARAMS_KEYS = [
81
81
  "tts_quality"
82
82
  ];
83
83
 
84
+ // models/capabilities.ts
85
+ function isManagedAgentModelCapability(capabilities) {
86
+ return capabilities.interaction === "agent";
87
+ }
88
+ function isDecisionModelCapability(capabilities) {
89
+ return capabilities.interaction === "decision";
90
+ }
91
+ function acceptsTextInstructions(capabilities) {
92
+ if (capabilities.interaction === "realtime") return true;
93
+ const input = capabilities.input ?? [];
94
+ return input.length === 0 || input.includes("text");
95
+ }
96
+
97
+ // models/feature-map.ts
98
+ var FEATURE_BUCKET_ORDER = [
99
+ "thinking",
100
+ "tools",
101
+ "schema",
102
+ "search",
103
+ "extraction",
104
+ "x_search",
105
+ "other"
106
+ ];
107
+ var FEATURE_BUCKETS = {
108
+ schema: { label: "Schema", description: "Structured & JSON output" },
109
+ tools: { label: "Tools", description: "Function & tool calling" },
110
+ search: { label: "Search", description: "Web search" },
111
+ x_search: { label: "X Search", description: "X (Twitter) platform search" },
112
+ thinking: { label: "Thinking", description: "Reasoning & extended thinking" },
113
+ extraction: {
114
+ label: "Extraction",
115
+ description: "NER, classification & entity extraction"
116
+ },
117
+ other: { label: "Other", description: "Uncategorized or niche features" }
118
+ };
119
+
120
+ // models/format.ts
121
+ var RATING_TO_TIER = {
122
+ 1: "$",
123
+ 2: "$$",
124
+ 3: "$$$",
125
+ 4: "$$$$",
126
+ 5: "$$$$$",
127
+ 6: "$$$$$+"
128
+ // renders as the "5+" band
129
+ };
130
+ var TOP_TIER_COST_RATING = 6;
131
+ var TOP_TIER_LOCKED_TITLE = "This model is limited to approved accounts";
132
+ function isTopTierRating(rating) {
133
+ return typeof rating === "number" && rating >= TOP_TIER_COST_RATING;
134
+ }
135
+ function costRatingTier(rating) {
136
+ if (typeof rating !== "number") return null;
137
+ return RATING_TO_TIER[rating] ?? null;
138
+ }
139
+ function speedRatingLabel(rating) {
140
+ if (typeof rating !== "number") return null;
141
+ return rating >= 6 ? "5+" : String(rating);
142
+ }
143
+ function speedRatingWord(rating) {
144
+ if (typeof rating !== "number") return "\u2014";
145
+ if (rating <= 2) return "Slow";
146
+ if (rating === 3) return "Med";
147
+ if (rating === 4) return "Fast";
148
+ if (rating === 5) return "Fast+";
149
+ return "Max";
150
+ }
151
+ function costRatingWord(rating) {
152
+ if (typeof rating !== "number") return "\u2014";
153
+ if (rating <= 1) return "Low";
154
+ if (rating === 2) return "Low+";
155
+ if (rating === 3) return "Med";
156
+ if (rating === 4) return "High";
157
+ if (rating === 5) return "High+";
158
+ return "Max";
159
+ }
160
+ function priceTierRating(tier) {
161
+ return tier.endsWith("+") ? 6 : tier.length;
162
+ }
163
+
164
+ // models/service-label.ts
165
+ var SERVICE_LABEL = "Service";
166
+ var SERVICE_LABEL_PLURAL = "Services";
167
+ function serviceCountLabel(count) {
168
+ const noun = (count === 1 ? SERVICE_LABEL : SERVICE_LABEL_PLURAL).toLowerCase();
169
+ return `${count} ${noun}`;
170
+ }
171
+
172
+ // models/catalog/types.ts
173
+ var INPUT_MODALITIES = ["text", "image", "audio", "video"];
174
+ var OUTPUT_MODALITIES = [
175
+ "text",
176
+ "image",
177
+ "audio",
178
+ "video",
179
+ "entities",
180
+ "decision"
181
+ ];
182
+
183
+ // models/succession.ts
184
+ var MAX_SUCCESSION_HOPS = 8;
185
+ function buildSuccessionIndex(models) {
186
+ return new Map(models.map((m) => [m.id, m]));
187
+ }
188
+ function isSupersedable(node) {
189
+ return node.isDeprecated || node.retiredAt != null;
190
+ }
191
+ var loggedBroken = /* @__PURE__ */ new Set();
192
+ function logBroken(modelId, reason) {
193
+ if (loggedBroken.has(modelId)) return;
194
+ loggedBroken.add(modelId);
195
+ console.warn(`[models/succession] ${modelId}: successor chain broken (${reason}); keeping the original model.`);
196
+ }
197
+ function resolveSuccessor(modelId, index) {
198
+ const start = index.get(modelId);
199
+ if (!start || !isSupersedable(start) || !start.successorId) return modelId;
200
+ const seen = /* @__PURE__ */ new Set([modelId]);
201
+ let current = start;
202
+ for (let hop = 0; hop < MAX_SUCCESSION_HOPS; hop += 1) {
203
+ const nextId = current.successorId;
204
+ if (!nextId) {
205
+ logBroken(modelId, `${current.id} is superseded and names no successor`);
206
+ return modelId;
207
+ }
208
+ if (seen.has(nextId)) {
209
+ logBroken(modelId, `cycle at ${nextId}`);
210
+ return modelId;
211
+ }
212
+ const next = index.get(nextId);
213
+ if (!next) {
214
+ logBroken(modelId, `successor ${nextId} is not in the catalog`);
215
+ return modelId;
216
+ }
217
+ if (!isSupersedable(next)) return next.id;
218
+ seen.add(nextId);
219
+ current = next;
220
+ }
221
+ logBroken(modelId, `more than ${MAX_SUCCESSION_HOPS} hops`);
222
+ return modelId;
223
+ }
224
+
225
+ // models/fit.ts
226
+ var featuresOf = (c) => {
227
+ const f = c.features;
228
+ if (!f) return [];
229
+ return Array.isArray(f) ? f : f.raw ?? [];
230
+ };
231
+ function seesImages(c) {
232
+ return (c.input ?? []).includes("image") || featuresOf(c).includes("vision");
233
+ }
234
+ function takesInput(c, modality) {
235
+ if (modality === "image") return seesImages(c);
236
+ return (c.input ?? []).includes(modality);
237
+ }
238
+ var MEDIA_GENERATION_FEATURES = /* @__PURE__ */ new Set(["image_generation", "video_generation", "audio_generation", "music_generation"]);
239
+ function isChatModelCapability(c) {
240
+ if (c.interaction !== "turn") return false;
241
+ if (!(c.input ?? []).includes("text")) return false;
242
+ if (!(c.output ?? []).includes("text")) return false;
243
+ return !featuresOf(c).some((f) => MEDIA_GENERATION_FEATURES.has(f));
244
+ }
245
+
84
246
  // settings/ui-gates.ts
85
247
  var UI_GATE_KEYS = [
86
248
  "tools",
@@ -179,357 +341,2269 @@ function readEffectiveSettings(entry) {
179
341
  return entry ? mergeEffectiveSettings(entry.defaults, entry.overrides) : EMPTY;
180
342
  }
181
343
 
182
- // settings/react/SettingControlInput.tsx
183
- import { useEffect, useState } from "react";
344
+ // settings/react/ModelSettingsPanel.tsx
345
+ import { Checkbox as Checkbox2 } from "@ai-matrx/design-system";
346
+ import { Button as Button2, Chip, Switch as Switch2 } from "@ai-matrx/design-system/controls";
347
+
348
+ // models/react/context.tsx
349
+ import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
350
+ import { jsx } from "react/jsx-runtime";
351
+ var ModelCatalogContext = createContext(null);
352
+ function useBinding(hook) {
353
+ const binding = useContext(ModelCatalogContext);
354
+ if (!binding) {
355
+ throw new Error(`${hook}() needs <ModelCatalogProvider catalog={createModelCatalog(...)} favorites={createModelFavorites(...)}> above it. A picker with no catalog is a lie on screen.`);
356
+ }
357
+ return binding;
358
+ }
359
+ var useModelCatalogUiPorts = () => useBinding("useModelCatalogUiPorts").ports;
360
+ function useModelCatalog(variant) {
361
+ const { catalog } = useBinding("useModelCatalog");
362
+ const state = useSyncExternalStore(catalog.subscribe, catalog.getState, catalog.getState);
363
+ useEffect(() => {
364
+ void catalog.load(variant);
365
+ }, [catalog, variant]);
366
+ const v = state[variant];
367
+ const loading = v.status === "idle" || v.status === "loading";
368
+ return {
369
+ models: loading ? [] : v.models,
370
+ isLoading: loading,
371
+ error: loading ? null : v.error,
372
+ reload: () => {
373
+ void catalog.reload(variant);
374
+ }
375
+ };
376
+ }
377
+ function useModelFavorites(active) {
378
+ const { favorites } = useBinding("useModelFavorites");
379
+ const state = useSyncExternalStore(favorites.subscribe, favorites.getState, favorites.getState);
380
+ useEffect(() => {
381
+ void favorites.reconcile();
382
+ }, [favorites, active]);
383
+ const favoriteSet = useMemo(() => new Set(state.favoriteIds), [state.favoriteIds]);
384
+ return { favoriteIds: state.favoriteIds, favoriteSet, toggleFavorite: favorites.toggle };
385
+ }
386
+ var NO_LOCAL_MODELS = [];
387
+ var noopSubscribe = () => () => {
388
+ };
389
+ function useLocalModels(override) {
390
+ const contextPort = useBinding("useLocalModels").ports.localModels;
391
+ const port = override ?? contextPort;
392
+ const models = useSyncExternalStore(
393
+ port ? port.subscribe : noopSubscribe,
394
+ port ? port.getModels : () => NO_LOCAL_MODELS,
395
+ port ? port.getModels : () => NO_LOCAL_MODELS
396
+ );
397
+ return { models, groupLabel: port?.groupLabel ?? "On this computer", open: port?.open ? (id) => port.open?.(id) : void 0 };
398
+ }
399
+
400
+ // models/react/ModelListDropdown.tsx
401
+ import { formatCount } from "@ai-matrx/kit/format";
402
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState } from "react";
184
403
  import {
185
- Checkbox,
186
- Input,
187
- Label,
188
- Select,
189
- SelectContent,
190
- SelectItem,
191
- SelectTrigger,
192
- SelectValue,
193
- Slider,
194
- Textarea,
404
+ cn,
405
+ Popover,
406
+ PopoverTrigger,
407
+ PopoverContent,
195
408
  Tooltip,
196
409
  TooltipContent,
197
410
  TooltipProvider,
198
- TooltipTrigger
411
+ TooltipTrigger,
412
+ selectTriggerVariants,
413
+ Drawer,
414
+ DrawerTrigger,
415
+ DrawerContent,
416
+ DrawerTitle,
417
+ useDialogContainer,
418
+ useIsMobile
199
419
  } from "@ai-matrx/design-system";
200
- import { AspectRatioSelect, OptionCombobox, isAspectRatioOptionSet } from "@ai-matrx/design-system/controls";
420
+ import { Input } from "@ai-matrx/design-system/controls";
421
+ import { MakerBrandGlyph, ServiceBrandGlyph } from "@ai-matrx/icons/brand-glyphs";
201
422
 
202
- // settings/setting-state.ts
203
- var ENUM_OFF_VALUE = "none";
204
- function isOffValue(value, control) {
205
- if (value === false) return true;
206
- if (value === ENUM_OFF_VALUE) {
207
- return !control || control.type === "enum";
423
+ // catalog/shared/icon-paths.ts
424
+ var SHAPES = {
425
+ star: {
426
+ paths: [
427
+ "M11.5 3.2a.6.6 0 0 1 1 0l2.3 4.7 5.2.8a.6.6 0 0 1 .3 1l-3.8 3.7.9 5.2a.6.6 0 0 1-.9.6L12 16.8l-4.6 2.4a.6.6 0 0 1-.9-.6l.9-5.2-3.8-3.7a.6.6 0 0 1 .3-1l5.2-.8Z"
428
+ ],
429
+ fillable: true
430
+ },
431
+ chevronRight: { paths: ["m9 18 6-6-6-6"] },
432
+ chevronDown: { paths: ["m6 9 6 6 6-6"] },
433
+ search: {
434
+ paths: ["m21 21-4.3-4.3"],
435
+ circles: [{ cx: 11, cy: 11, r: 8 }]
436
+ },
437
+ x: { paths: ["M18 6 6 18", "m6 6 12 12"] },
438
+ check: { paths: ["M20 6 9 17l-5-5"] },
439
+ arrowUpDown: {
440
+ paths: ["m21 16-4 4-4-4", "M17 20V4", "m3 8 4-4 4 4", "M7 4v16"]
441
+ },
442
+ folder: {
443
+ paths: [
444
+ "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
445
+ ]
446
+ },
447
+ tag: {
448
+ paths: [
449
+ "M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z"
450
+ ],
451
+ circles: [{ cx: 7.5, cy: 7.5, r: 0.5, filled: true }]
452
+ },
453
+ rotateCcw: {
454
+ paths: [
455
+ "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",
456
+ "M3 3v5h5"
457
+ ]
458
+ },
459
+ loader: { paths: ["M21 12a9 9 0 1 1-6.219-8.56"] },
460
+ alertTriangle: {
461
+ paths: [
462
+ "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
463
+ "M12 9v4",
464
+ "M12 17h.01"
465
+ ]
466
+ },
467
+ /**
468
+ * The Orchestra marker (Lucide `music-4`): the glyph the Workflow Studio's
469
+ * picker used for a conductor, kept identical so the same agent wears the
470
+ * same mark in every client.
471
+ */
472
+ music4: {
473
+ paths: ["M9 18V5l12-2v13", "m9 9 12-2"],
474
+ circles: [
475
+ { cx: 6, cy: 18, r: 3 },
476
+ { cx: 18, cy: 16, r: 3 }
477
+ ]
478
+ },
479
+ clock: {
480
+ paths: ["M12 6v6l4 2"],
481
+ circles: [{ cx: 12, cy: 12, r: 10 }]
482
+ },
483
+ globe: {
484
+ paths: ["M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", "M2 12h20"],
485
+ circles: [{ cx: 12, cy: 12, r: 10 }]
486
+ },
487
+ users: {
488
+ paths: [
489
+ "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",
490
+ "M22 21v-2a4 4 0 0 0-3-3.87",
491
+ "M16 3.13a4 4 0 0 1 0 7.75"
492
+ ],
493
+ circles: [{ cx: 9, cy: 7, r: 4 }]
494
+ },
495
+ externalLink: {
496
+ paths: [
497
+ "M15 3h6v6",
498
+ "M10 14 21 3",
499
+ "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"
500
+ ]
501
+ },
502
+ messageCircle: { paths: ["M7.9 20A9 9 0 1 0 4 16.1L2 22Z"] },
503
+ rocket: {
504
+ paths: [
505
+ "M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0",
506
+ "m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2",
507
+ "M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0",
508
+ "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"
509
+ ]
510
+ },
511
+ /**
512
+ * THE ARCHIVE GLYPH (Lucide `archive`) — the ONE mark of the platform
513
+ * archive filter, drawn identically in every Matrx client so the control a
514
+ * person learns on one surface is recognizable on all of them.
515
+ */
516
+ archive: {
517
+ paths: [
518
+ "M3 3h18a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z",
519
+ "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",
520
+ "M10 12h4"
521
+ ]
522
+ },
523
+ circleCheck: {
524
+ paths: ["m9 12 2 2 4-4"],
525
+ circles: [{ cx: 12, cy: 12, r: 10 }]
208
526
  }
209
- return false;
527
+ };
528
+ var ICON_PATHS = SHAPES;
529
+ var ICON_NAMES = Object.keys(ICON_PATHS);
530
+
531
+ // catalog/react/icons.tsx
532
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
533
+ function Svg({ children, ...props }) {
534
+ return /* @__PURE__ */ jsx2(
535
+ "svg",
536
+ {
537
+ xmlns: "http://www.w3.org/2000/svg",
538
+ viewBox: "0 0 24 24",
539
+ fill: "none",
540
+ stroke: "currentColor",
541
+ strokeWidth: 2,
542
+ strokeLinecap: "round",
543
+ strokeLinejoin: "round",
544
+ "aria-hidden": "true",
545
+ ...props,
546
+ children
547
+ }
548
+ );
549
+ }
550
+ function Shape({ name, filled }) {
551
+ const shape = ICON_PATHS[name];
552
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
553
+ shape.paths.map((d, index) => /* @__PURE__ */ jsx2(
554
+ "path",
555
+ {
556
+ d,
557
+ ...filled && shape.fillable ? { fill: "currentColor" } : {}
558
+ },
559
+ `p${index}`
560
+ )),
561
+ (shape.circles ?? []).map((circle, index) => /* @__PURE__ */ jsx2(
562
+ "circle",
563
+ {
564
+ cx: circle.cx,
565
+ cy: circle.cy,
566
+ r: circle.r,
567
+ ...circle.filled ? { fill: "currentColor" } : {}
568
+ },
569
+ `c${index}`
570
+ ))
571
+ ] });
572
+ }
573
+ function makeIcon(name) {
574
+ return function Icon(props) {
575
+ return /* @__PURE__ */ jsx2(Svg, { ...props, children: /* @__PURE__ */ jsx2(Shape, { name, filled: false }) });
576
+ };
577
+ }
578
+ var ArrowUpDownIcon = makeIcon("arrowUpDown");
579
+ var FolderIcon = makeIcon("folder");
580
+ var TagIcon = makeIcon("tag");
581
+ var RotateCcwIcon = makeIcon("rotateCcw");
582
+ var LoaderIcon = makeIcon("loader");
583
+ var UsersIcon = makeIcon("users");
584
+ var MessageCircleIcon = makeIcon("messageCircle");
585
+ var RocketIcon = makeIcon("rocket");
586
+ var CircleCheckIcon = makeIcon("circleCheck");
587
+ var AlertTriangleIcon = makeIcon("alertTriangle");
588
+ var Music4Icon = makeIcon("music4");
589
+ function MousePointerClickIcon(props) {
590
+ return /* @__PURE__ */ jsxs(Svg, { ...props, children: [
591
+ /* @__PURE__ */ jsx2("path", { d: "M14 4.1 12 6" }),
592
+ /* @__PURE__ */ jsx2("path", { d: "m5.1 8-2.9-.8" }),
593
+ /* @__PURE__ */ jsx2("path", { d: "m6 12-1.9 2" }),
594
+ /* @__PURE__ */ jsx2("path", { d: "M7.2 2.2 8 5.1" }),
595
+ /* @__PURE__ */ jsx2("path", { d: "M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z" })
596
+ ] });
210
597
  }
211
598
 
212
- // settings/react/SettingControlInput.tsx
213
- import { jsx, jsxs } from "react/jsx-runtime";
214
- var SELECT_MAX_OPTIONS = 12;
215
- function choiceFor(options) {
216
- if (isAspectRatioOptionSet(options)) return "aspect-ratio";
217
- if (options.length > SELECT_MAX_OPTIONS) return "searchable";
218
- return "select";
599
+ // models/react/icons.tsx
600
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
601
+ function Svg2({ children, ...props }) {
602
+ return /* @__PURE__ */ jsx3(
603
+ "svg",
604
+ {
605
+ xmlns: "http://www.w3.org/2000/svg",
606
+ viewBox: "0 0 24 24",
607
+ fill: "none",
608
+ stroke: "currentColor",
609
+ strokeWidth: 2,
610
+ strokeLinecap: "round",
611
+ strokeLinejoin: "round",
612
+ "aria-hidden": "true",
613
+ ...props,
614
+ children
615
+ }
616
+ );
219
617
  }
220
- function WarningIcon({ className }) {
221
- return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className, children: [
222
- /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
223
- /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
224
- /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
618
+ function ShieldIcon(props) {
619
+ return /* @__PURE__ */ jsx3(Svg2, { ...props, children: /* @__PURE__ */ jsx3("path", { d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" }) });
620
+ }
621
+ function SlidersHorizontalIcon(props) {
622
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
623
+ /* @__PURE__ */ jsx3("path", { d: "M10 5H3" }),
624
+ /* @__PURE__ */ jsx3("path", { d: "M12 19H3" }),
625
+ /* @__PURE__ */ jsx3("path", { d: "M14 3v4" }),
626
+ /* @__PURE__ */ jsx3("path", { d: "M16 17v4" }),
627
+ /* @__PURE__ */ jsx3("path", { d: "M21 12h-9" }),
628
+ /* @__PURE__ */ jsx3("path", { d: "M21 19h-5" }),
629
+ /* @__PURE__ */ jsx3("path", { d: "M21 5h-7" }),
630
+ /* @__PURE__ */ jsx3("path", { d: "M8 10v4" }),
631
+ /* @__PURE__ */ jsx3("path", { d: "M8 12H3" })
225
632
  ] });
226
633
  }
227
- function flattenTypeObject(value) {
228
- if (typeof value === "object" && value !== null && !Array.isArray(value) && "type" in value && Object.keys(value).length === 1) {
229
- return value.type;
230
- }
231
- return value;
634
+ function TypeIcon(props) {
635
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
636
+ /* @__PURE__ */ jsx3("path", { d: "M12 4v16" }),
637
+ /* @__PURE__ */ jsx3("path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" }),
638
+ /* @__PURE__ */ jsx3("path", { d: "M9 20h6" })
639
+ ] });
232
640
  }
233
- function SettingControlInput({
234
- settingKey,
235
- control,
641
+ function ImageIcon(props) {
642
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
643
+ /* @__PURE__ */ jsx3("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
644
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "2" }),
645
+ /* @__PURE__ */ jsx3("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
646
+ ] });
647
+ }
648
+ function AudioLinesIcon(props) {
649
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
650
+ /* @__PURE__ */ jsx3("path", { d: "M2 10v3" }),
651
+ /* @__PURE__ */ jsx3("path", { d: "M6 6v11" }),
652
+ /* @__PURE__ */ jsx3("path", { d: "M10 3v18" }),
653
+ /* @__PURE__ */ jsx3("path", { d: "M14 8v7" }),
654
+ /* @__PURE__ */ jsx3("path", { d: "M18 5v13" }),
655
+ /* @__PURE__ */ jsx3("path", { d: "M22 10v3" })
656
+ ] });
657
+ }
658
+ function BoxesIcon(props) {
659
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
660
+ /* @__PURE__ */ jsx3("path", { d: "M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" }),
661
+ /* @__PURE__ */ jsx3("path", { d: "m7 16.5-4.74-2.85" }),
662
+ /* @__PURE__ */ jsx3("path", { d: "m7 16.5 5-3" }),
663
+ /* @__PURE__ */ jsx3("path", { d: "M7 16.5v5.17" }),
664
+ /* @__PURE__ */ jsx3("path", { d: "M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" }),
665
+ /* @__PURE__ */ jsx3("path", { d: "m17 16.5-5-3" }),
666
+ /* @__PURE__ */ jsx3("path", { d: "m17 16.5 4.74-2.85" }),
667
+ /* @__PURE__ */ jsx3("path", { d: "M17 16.5v5.17" }),
668
+ /* @__PURE__ */ jsx3("path", { d: "M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" }),
669
+ /* @__PURE__ */ jsx3("path", { d: "M12 8 7.26 5.15" }),
670
+ /* @__PURE__ */ jsx3("path", { d: "m12 8 4.74-2.85" }),
671
+ /* @__PURE__ */ jsx3("path", { d: "M12 13.5V8" })
672
+ ] });
673
+ }
674
+ function FileTextIcon(props) {
675
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
676
+ /* @__PURE__ */ jsx3("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }),
677
+ /* @__PURE__ */ jsx3("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }),
678
+ /* @__PURE__ */ jsx3("path", { d: "M10 9H8" }),
679
+ /* @__PURE__ */ jsx3("path", { d: "M16 13H8" }),
680
+ /* @__PURE__ */ jsx3("path", { d: "M16 17H8" })
681
+ ] });
682
+ }
683
+ function LayersIcon(props) {
684
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
685
+ /* @__PURE__ */ jsx3("path", { d: "M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z" }),
686
+ /* @__PURE__ */ jsx3("path", { d: "M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12" }),
687
+ /* @__PURE__ */ jsx3("path", { d: "M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17" })
688
+ ] });
689
+ }
690
+ function ScaleIcon(props) {
691
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
692
+ /* @__PURE__ */ jsx3("path", { d: "M12 3v18" }),
693
+ /* @__PURE__ */ jsx3("path", { d: "m19 8 3 8a5 5 0 0 1-6 0zV7" }),
694
+ /* @__PURE__ */ jsx3("path", { d: "M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1" }),
695
+ /* @__PURE__ */ jsx3("path", { d: "m5 8 3 8a5 5 0 0 1-6 0zV7" }),
696
+ /* @__PURE__ */ jsx3("path", { d: "M7 21h10" })
697
+ ] });
698
+ }
699
+ function LanguagesIcon(props) {
700
+ return /* @__PURE__ */ jsxs2(Svg2, { ...props, children: [
701
+ /* @__PURE__ */ jsx3("path", { d: "m5 8 6 6" }),
702
+ /* @__PURE__ */ jsx3("path", { d: "m4 14 6-6 2-3" }),
703
+ /* @__PURE__ */ jsx3("path", { d: "M2 5h12" }),
704
+ /* @__PURE__ */ jsx3("path", { d: "M7 2h1" }),
705
+ /* @__PURE__ */ jsx3("path", { d: "m22 22-5-10-5 10" }),
706
+ /* @__PURE__ */ jsx3("path", { d: "M14 18h6" })
707
+ ] });
708
+ }
709
+
710
+ // models/react/ModelListDropdown.tsx
711
+ import { ChevronDownIcon, ChevronRightIcon, SearchIcon, StarIcon, VideoIcon } from "@ai-matrx/design-system/icons";
712
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
713
+ var PANEL_HEIGHT = 640;
714
+ var LIST_MAX_HEIGHT = `min(${PANEL_HEIGHT}px, var(--radix-popper-available-height, 70dvh))`;
715
+ var LIST_WIDTH = 520;
716
+ var DETAIL_WIDTH = 540;
717
+ var INLINE_LIST_MIN_WIDTH = 400;
718
+ var INLINE_DETAIL_MIN_WIDTH = 280;
719
+ var ADMIN_PANEL_WIDTH = 400;
720
+ var ADMIN_ONLY_SORTS = /* @__PURE__ */ new Set([
721
+ "vendor",
722
+ "api",
723
+ "deprecated",
724
+ "availability",
725
+ "premium"
726
+ ]);
727
+ var MODALITY_ICON = {
728
+ text: TypeIcon,
729
+ image: ImageIcon,
730
+ audio: AudioLinesIcon,
731
+ video: VideoIcon,
732
+ document: FileTextIcon,
733
+ entities: BoxesIcon,
734
+ embedding: LayersIcon,
735
+ decision: ScaleIcon
736
+ };
737
+ var INTERACTION_LABEL = {
738
+ turn: "Standard",
739
+ single: "Single-shot",
740
+ extraction: "Extraction",
741
+ realtime: "Realtime",
742
+ embedding: "Embedding",
743
+ agent: "Background agent",
744
+ decision: "Decision",
745
+ adapter: "Adapter",
746
+ detection: "Detector"
747
+ };
748
+ function SpeedDots({ value }) {
749
+ const label = speedRatingLabel(value);
750
+ return /* @__PURE__ */ jsxs3(
751
+ "span",
752
+ {
753
+ className: "inline-flex items-center gap-1",
754
+ title: label == null ? "No speed rating yet" : `Speed ${label}/5`,
755
+ children: [
756
+ /* @__PURE__ */ jsx4("span", { className: "text-[11px] font-medium tabular-nums text-foreground/80", children: speedRatingWord(value) }),
757
+ /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-0.5", children: [
758
+ [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx4(
759
+ "span",
760
+ {
761
+ className: cn(
762
+ "h-1.5 w-1.5 rounded-full",
763
+ value != null && i < value ? "bg-foreground/70" : "bg-muted-foreground/25"
764
+ )
765
+ },
766
+ i
767
+ )),
768
+ value != null && value >= 6 && /* @__PURE__ */ jsx4("span", { className: "text-[9px] leading-none text-amber-500", children: "+" })
769
+ ] })
770
+ ]
771
+ }
772
+ );
773
+ }
774
+ function SpeedCell({ value }) {
775
+ const word = speedRatingWord(value);
776
+ const label = speedRatingLabel(value);
777
+ return /* @__PURE__ */ jsx4(
778
+ DelayedHint,
779
+ {
780
+ label: label == null ? "No speed rating yet" : `Speed ${word} (${label}/5)`,
781
+ children: /* @__PURE__ */ jsx4("span", { className: "inline-block w-11 text-right text-[11px] font-medium tabular-nums text-foreground/80", children: word })
782
+ }
783
+ );
784
+ }
785
+ function CostTier({ tier }) {
786
+ if (!tier)
787
+ return /* @__PURE__ */ jsx4("span", { className: "text-[11px] text-muted-foreground/50", children: "\u2014" });
788
+ const rating = priceTierRating(tier);
789
+ const word = costRatingWord(rating);
790
+ return /* @__PURE__ */ jsx4(
791
+ DelayedHint,
792
+ {
793
+ label: `Relative cost ${word} (${rating >= 6 ? "5+" : rating}/5 compared with other models)`,
794
+ children: /* @__PURE__ */ jsx4("span", { className: "inline-block w-11 text-right text-[11px] font-medium tabular-nums text-foreground/80", children: word })
795
+ }
796
+ );
797
+ }
798
+ function ModalityIcons({ list }) {
799
+ if (list.length === 0)
800
+ return /* @__PURE__ */ jsx4("span", { className: "text-[11px] text-muted-foreground/50", children: "\u2014" });
801
+ return /* @__PURE__ */ jsx4("span", { className: "inline-flex flex-nowrap items-center gap-1 whitespace-nowrap", children: list.map((m) => {
802
+ const Icon = MODALITY_ICON[m];
803
+ return /* @__PURE__ */ jsxs3(
804
+ "span",
805
+ {
806
+ className: "inline-flex shrink-0 items-center gap-0.5 text-xs text-foreground/70",
807
+ title: m,
808
+ children: [
809
+ /* @__PURE__ */ jsx4(Icon, { className: "h-3 w-3" }),
810
+ /* @__PURE__ */ jsx4("span", { className: "capitalize", children: m })
811
+ ]
812
+ },
813
+ m
814
+ );
815
+ }) });
816
+ }
817
+ function AdminRow({
818
+ label,
236
819
  value,
237
- onChange,
238
- disabled = false,
239
- id
820
+ mono
240
821
  }) {
241
- const inputId = id ?? `setting-control-${settingKey}`;
242
- if (control.type === "enum" && control.enum?.length) {
243
- const flattened = settingKey === "response_format" ? flattenTypeObject(value) : value;
244
- const stringValue = flattened === void 0 || flattened === null ? "" : String(flattened);
245
- const isValueMismatch = stringValue !== "" && !control.enum.includes(stringValue);
246
- const emit = (v) => onChange(settingKey === "response_format" ? { type: v } : v);
247
- const choice = choiceFor(control.enum);
248
- const picker = choice === "aspect-ratio" ? /* @__PURE__ */ jsx(
249
- AspectRatioSelect,
250
- {
251
- value: isValueMismatch ? "" : stringValue,
252
- onChange: emit,
253
- options: control.enum,
254
- variant: "inline",
255
- disabled,
256
- id: inputId,
257
- placeholder: "Default"
258
- }
259
- ) : choice === "searchable" ? /* @__PURE__ */ jsx(
260
- OptionCombobox,
822
+ return /* @__PURE__ */ jsxs3("div", { className: "flex items-baseline gap-2 whitespace-nowrap", children: [
823
+ /* @__PURE__ */ jsx4("span", { className: "w-[76px] shrink-0 text-[10px] uppercase tracking-wide text-muted-foreground", children: label }),
824
+ /* @__PURE__ */ jsx4(
825
+ "span",
261
826
  {
262
- value: isValueMismatch ? "" : stringValue,
263
- onChange: emit,
264
- options: control.enum,
265
- variant: "inline",
266
- disabled,
267
- id: inputId,
268
- placeholder: "Default"
827
+ className: cn(
828
+ "min-w-0 truncate text-[11px] text-foreground/90",
829
+ mono && "font-mono"
830
+ ),
831
+ title: value ?? void 0,
832
+ children: value ?? "\u2014"
269
833
  }
270
- ) : null;
271
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-1.5", children: [
272
- picker ?? /* @__PURE__ */ jsxs(
273
- Select,
834
+ )
835
+ ] });
836
+ }
837
+ function fmtUsd(n) {
838
+ return n == null ? "\u2014" : `$${n}`;
839
+ }
840
+ function fmtPoints(n) {
841
+ return n == null ? "\u2014" : n.toLocaleString();
842
+ }
843
+ function fmtPointsCompact(n) {
844
+ return formatCount(n, { style: "compact" });
845
+ }
846
+ function AdminOfferingBlock({
847
+ offering,
848
+ index,
849
+ total
850
+ }) {
851
+ const unit = offering.usageBasis ? `/ ${offering.usageBasis}` : "/ 1M tokens";
852
+ return /* @__PURE__ */ jsxs3("div", { className: "rounded-md border border-border p-2", children: [
853
+ /* @__PURE__ */ jsxs3("div", { className: "mb-1.5 flex items-center justify-between gap-2", children: [
854
+ /* @__PURE__ */ jsxs3("span", { className: "truncate text-[11px] font-semibold text-foreground", children: [
855
+ "Offering ",
856
+ index + 1,
857
+ " of ",
858
+ total,
859
+ index === 0 ? " \u2014 preferred" : ""
860
+ ] }),
861
+ /* @__PURE__ */ jsx4(
862
+ "span",
274
863
  {
275
- value: isValueMismatch ? "" : stringValue,
276
- onValueChange: emit,
277
- disabled,
278
- children: [
279
- /* @__PURE__ */ jsx(
280
- SelectTrigger,
281
- {
282
- id: inputId,
283
- className: "h-7 flex-1 border-0 bg-transparent px-1 text-xs font-medium text-foreground shadow-none hover:bg-transparent focus:ring-0 pointer-coarse:h-11 [&_svg]:h-3 [&_svg]:w-3",
284
- children: /* @__PURE__ */ jsx(SelectValue, { placeholder: isValueMismatch ? stringValue : "Default" })
285
- }
286
- ),
287
- /* @__PURE__ */ jsx(SelectContent, { className: "text-xs", children: control.enum.map((option) => /* @__PURE__ */ jsx(
288
- SelectItem,
289
- {
290
- value: option,
291
- className: "py-1 text-xs",
292
- children: option === ENUM_OFF_VALUE ? "Off" : option
293
- },
294
- option
295
- )) })
296
- ]
864
+ className: cn(
865
+ "shrink-0 rounded px-1.5 py-0.5 text-[9px] font-medium",
866
+ offering.isAvailable ? "bg-primary/10 text-foreground/80" : "bg-destructive/15 text-destructive-ink"
867
+ ),
868
+ children: offering.isAvailable ? "Available" : "Unavailable"
869
+ }
870
+ )
871
+ ] }),
872
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-0.5", children: [
873
+ /* @__PURE__ */ jsx4(AdminRow, { label: "Model", value: offering.providerModelId, mono: true }),
874
+ /* @__PURE__ */ jsx4(AdminRow, { label: "Vendor", value: offering.vendor, mono: true }),
875
+ /* @__PURE__ */ jsx4(AdminRow, { label: "API", value: offering.apiName, mono: true }),
876
+ /* @__PURE__ */ jsx4(AdminRow, { label: "Translator", value: offering.translatorKey, mono: true }),
877
+ /* @__PURE__ */ jsx4(AdminRow, { label: "Transport", value: offering.transport, mono: true }),
878
+ /* @__PURE__ */ jsx4(
879
+ AdminRow,
880
+ {
881
+ label: "Endpoint",
882
+ value: offering.endpointDisplayName || offering.endpointInternalName ? `${offering.endpointDisplayName ?? "\u2014"} (${offering.endpointInternalName ?? "\u2014"})` : null
297
883
  }
298
884
  ),
299
- isValueMismatch && /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
300
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "shrink-0 cursor-help text-amber-500", children: /* @__PURE__ */ jsx(WarningIcon, { className: "h-3.5 w-3.5" }) }) }),
301
- /* @__PURE__ */ jsxs(TooltipContent, { side: "top", className: "max-w-[220px] type-secondary", children: [
302
- '"',
303
- stringValue,
304
- '" is not a recognized option for this model'
305
- ] })
306
- ] }) })
307
- ] });
308
- }
309
- if (control.type === "boolean") {
310
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
311
- /* @__PURE__ */ jsx(
312
- Checkbox,
885
+ /* @__PURE__ */ jsx4(AdminRow, { label: "Priority", value: String(offering.priority), mono: true }),
886
+ /* @__PURE__ */ jsx4(
887
+ AdminRow,
313
888
  {
314
- id: inputId,
315
- checked: value === true,
316
- onCheckedChange: (c) => onChange(c === true),
317
- disabled,
318
- className: "cursor-pointer"
889
+ label: "Points in",
890
+ value: offering.pointsInput == null ? null : `${fmtPoints(offering.pointsInput)} / 1M`,
891
+ mono: true
319
892
  }
320
893
  ),
321
- /* @__PURE__ */ jsx(
322
- Label,
894
+ /* @__PURE__ */ jsx4(
895
+ AdminRow,
323
896
  {
324
- htmlFor: inputId,
325
- className: "cursor-pointer text-xs text-muted-foreground",
326
- children: value === true ? "On" : "Off"
897
+ label: "Points out",
898
+ value: offering.pointsOutput == null ? null : `${fmtPoints(offering.pointsOutput)} / 1M`,
899
+ mono: true
327
900
  }
328
- )
329
- ] });
330
- }
331
- if (control.type === "number" || control.type === "integer") {
332
- const numeric = typeof value === "number" ? value : void 0;
333
- const hasRange = control.min !== void 0 && control.max !== void 0;
334
- return /* @__PURE__ */ jsx(
335
- NumberInput,
336
- {
337
- value: numeric,
338
- placeholderValue: typeof control.default === "number" ? control.default : void 0,
339
- onChange,
340
- onSliderChange: hasRange ? onChange : void 0,
341
- min: control.min,
342
- max: control.max,
343
- step: control.type === "integer" ? 1 : 0.01,
344
- isInteger: control.type === "integer",
345
- disabled,
346
- withSlider: hasRange
347
- }
348
- );
349
- }
350
- if (control.type === "string_array") {
351
- const arrayValue = Array.isArray(value) ? value.join("\n") : "";
352
- return /* @__PURE__ */ jsx(
353
- Textarea,
901
+ ),
902
+ offering.pointsCachedInput != null && /* @__PURE__ */ jsx4(
903
+ AdminRow,
904
+ {
905
+ label: "Points cached",
906
+ value: `${fmtPoints(offering.pointsCachedInput)} / 1M`,
907
+ mono: true
908
+ }
909
+ ),
910
+ offering.pricing.length === 0 ? /* @__PURE__ */ jsx4(AdminRow, { label: "Pricing", value: "\u2014 (no pricing rows)" }) : offering.pricing.map((band, i) => /* @__PURE__ */ jsxs3("div", { className: "space-y-0.5", children: [
911
+ band.maxTokens != null && /* @__PURE__ */ jsx4(
912
+ AdminRow,
913
+ {
914
+ label: `Band ${i + 1}`,
915
+ value: `\u2264 ${formatCount(band.maxTokens)} tokens`
916
+ }
917
+ ),
918
+ /* @__PURE__ */ jsx4(
919
+ AdminRow,
920
+ {
921
+ label: "Input $",
922
+ value: `${fmtUsd(band.inputPrice)} ${unit}`,
923
+ mono: true
924
+ }
925
+ ),
926
+ /* @__PURE__ */ jsx4(
927
+ AdminRow,
928
+ {
929
+ label: "Output $",
930
+ value: `${fmtUsd(band.outputPrice)} ${unit}`,
931
+ mono: true
932
+ }
933
+ ),
934
+ /* @__PURE__ */ jsx4(
935
+ AdminRow,
936
+ {
937
+ label: "Cached $",
938
+ value: `${fmtUsd(band.cachedInputPrice)} ${unit}`,
939
+ mono: true
940
+ }
941
+ )
942
+ ] }, i)),
943
+ /* @__PURE__ */ jsx4(
944
+ AdminRow,
945
+ {
946
+ label: "Billing",
947
+ value: offering.usageBasis ? `Usage basis: ${offering.usageBasis}` : offering.tokenBilled ? "Token-billed (provider-reported tokens)" : "Standard $ / 1M tokens"
948
+ }
949
+ ),
950
+ /* @__PURE__ */ jsx4(AdminRow, { label: "Offering", value: offering.offeringId, mono: true })
951
+ ] })
952
+ ] });
953
+ }
954
+ function AdminOfferingsSection({ model }) {
955
+ const offerings = model.admin?.offerings ?? [];
956
+ return /* @__PURE__ */ jsxs3("div", { className: "flex h-full flex-col", children: [
957
+ /* @__PURE__ */ jsxs3("div", { className: "shrink-0 border-b border-border px-3 py-2 text-[10px] uppercase tracking-wide text-muted-foreground", children: [
958
+ "Offerings \u2014 real vendor / wire data (",
959
+ offerings.length,
960
+ ")"
961
+ ] }),
962
+ /* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto p-2", children: offerings.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-2 text-[11px] text-destructive-ink", children: "No offerings \u2014 this model is not callable (a model without an `ai.offering` row cannot be routed)." }) : offerings.map((o, i) => /* @__PURE__ */ jsx4(
963
+ AdminOfferingBlock,
354
964
  {
355
- id: inputId,
356
- value: arrayValue,
357
- onChange: (e) => onChange(e.target.value.split("\n").filter((s) => s.trim())),
358
- disabled,
359
- className: "min-h-14 px-2 py-1.5 font-mono text-base leading-snug disabled:opacity-50 sm:text-xs",
360
- placeholder: "One value per line..."
361
- }
362
- );
363
- }
364
- if (control.type === "array" || control.type === "object_array" || typeof value === "object" && value !== null) {
365
- return /* @__PURE__ */ jsx(
366
- JsonValueInput,
965
+ offering: o,
966
+ index: i,
967
+ total: offerings.length
968
+ },
969
+ o.offeringId
970
+ )) })
971
+ ] });
972
+ }
973
+ var SERVICE_CHIP_SELECTED = "rounded border border-primary/40 bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium text-foreground";
974
+ var SERVICE_CHIP_UNSELECTED = "rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground/80";
975
+ function ModelDetailCard({
976
+ model,
977
+ tier,
978
+ variant,
979
+ onSelect,
980
+ isCurrentModel,
981
+ pinnedOfferingId,
982
+ onPinOffering,
983
+ includeAdminSection
984
+ }) {
985
+ const pinnedHere = (t) => !!isCurrentModel && !!pinnedOfferingId && t.offeringId === pinnedOfferingId;
986
+ const anyPinnedHere = model.tiers.some(pinnedHere);
987
+ return /* @__PURE__ */ jsxs3("div", { className: "flex h-full flex-col", children: [
988
+ /* @__PURE__ */ jsxs3("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: [
989
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-start justify-between gap-2", children: [
990
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
991
+ /* @__PURE__ */ jsx4("div", { className: "truncate text-sm font-semibold text-foreground", children: model.name }),
992
+ model.maker && /* @__PURE__ */ jsxs3("div", { className: "mt-0.5 flex items-center gap-1 text-xs text-muted-foreground", children: [
993
+ /* @__PURE__ */ jsx4(
994
+ MakerBrandGlyph,
995
+ {
996
+ maker: model.maker,
997
+ colored: true,
998
+ className: "h-3.5 w-3.5"
999
+ }
1000
+ ),
1001
+ /* @__PURE__ */ jsx4("span", { children: model.maker })
1002
+ ] })
1003
+ ] }),
1004
+ /* @__PURE__ */ jsxs3("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [
1005
+ model.isPrimary && /* @__PURE__ */ jsx4("span", { className: "rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-foreground/70", children: "Default" }),
1006
+ model.isPremium && /* @__PURE__ */ jsx4("span", { className: "rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-foreground/70", children: "Premium" }),
1007
+ model.isDeprecated && !model.retiredAt && /* @__PURE__ */ jsx4(
1008
+ "span",
1009
+ {
1010
+ className: "rounded bg-destructive/15 px-1.5 py-0.5 text-[10px] font-medium text-destructive-ink",
1011
+ title: "Still runs normally; hidden from default selection.",
1012
+ children: "Deprecated"
1013
+ }
1014
+ ),
1015
+ model.retiredAt && /* @__PURE__ */ jsx4(
1016
+ "span",
1017
+ {
1018
+ className: "rounded bg-destructive/15 px-1.5 py-0.5 text-[10px] font-medium text-destructive-ink",
1019
+ title: `The provider no longer serves this model (retired ${model.retiredAt.slice(0, 10)}). It cannot be selected.`,
1020
+ children: "Retired"
1021
+ }
1022
+ )
1023
+ ] })
1024
+ ] }),
1025
+ model.description && /* @__PURE__ */ jsx4("p", { className: "mt-2 line-clamp-3 text-xs text-muted-foreground", children: model.description }),
1026
+ /* @__PURE__ */ jsxs3("dl", { className: "mt-3 space-y-2 text-xs", children: [
1027
+ /* @__PURE__ */ jsxs3("div", { children: [
1028
+ /* @__PURE__ */ jsx4("dt", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: "Input" }),
1029
+ /* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4(ModalityIcons, { list: model.input }) })
1030
+ ] }),
1031
+ /* @__PURE__ */ jsxs3("div", { children: [
1032
+ /* @__PURE__ */ jsx4("dt", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: "Output" }),
1033
+ /* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4(ModalityIcons, { list: model.output }) })
1034
+ ] }),
1035
+ model.features.buckets.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
1036
+ /* @__PURE__ */ jsx4("dt", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: "Features" }),
1037
+ /* @__PURE__ */ jsx4("dd", { className: "flex flex-wrap gap-1", children: model.features.buckets.map((b) => /* @__PURE__ */ jsx4(
1038
+ "span",
1039
+ {
1040
+ className: "whitespace-nowrap rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground/80",
1041
+ title: FEATURE_BUCKETS[b].description,
1042
+ children: FEATURE_BUCKETS[b].label
1043
+ },
1044
+ b
1045
+ )) })
1046
+ ] }),
1047
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-2", children: [
1048
+ /* @__PURE__ */ jsxs3("div", { className: "col-span-2", children: [
1049
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Points / 1M tokens" }),
1050
+ /* @__PURE__ */ jsx4(
1051
+ "dd",
1052
+ {
1053
+ className: "whitespace-nowrap text-sm font-semibold tabular-nums text-foreground",
1054
+ title: "Points per 1 million tokens \u2014 what usage actually costs you",
1055
+ children: model.pointsInput == null && model.pointsOutput == null ? "\u2014" : `${fmtPoints(model.pointsInput)} in \xB7 ${fmtPoints(model.pointsOutput)} out`
1056
+ }
1057
+ )
1058
+ ] }),
1059
+ /* @__PURE__ */ jsxs3("div", { children: [
1060
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Context window" }),
1061
+ /* @__PURE__ */ jsx4("dd", { className: "tabular-nums text-foreground/80", children: model.contextWindow?.toLocaleString() ?? "\u2014" })
1062
+ ] }),
1063
+ /* @__PURE__ */ jsxs3("div", { children: [
1064
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Max output" }),
1065
+ /* @__PURE__ */ jsx4("dd", { className: "tabular-nums text-foreground/80", children: model.maxTokens?.toLocaleString() ?? "\u2014" })
1066
+ ] }),
1067
+ /* @__PURE__ */ jsxs3("div", { children: [
1068
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Cost" }),
1069
+ /* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4(CostTier, { tier }) })
1070
+ ] }),
1071
+ /* @__PURE__ */ jsxs3("div", { children: [
1072
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Speed" }),
1073
+ /* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4(SpeedDots, { value: model.speedRating }) })
1074
+ ] }),
1075
+ /* @__PURE__ */ jsxs3("div", { children: [
1076
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Interaction" }),
1077
+ /* @__PURE__ */ jsx4("dd", { className: "text-foreground/80", children: INTERACTION_LABEL[model.interaction] })
1078
+ ] })
1079
+ ] }),
1080
+ model.tiers[0]?.servedVia && /* @__PURE__ */ jsxs3("div", { children: [
1081
+ /* @__PURE__ */ jsx4("dt", { className: "mb-0.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: model.tiers.length === 1 ? SERVICE_LABEL : SERVICE_LABEL_PLURAL }),
1082
+ /* @__PURE__ */ jsx4("dd", { className: "flex flex-wrap gap-1", children: onPinOffering ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
1083
+ /* @__PURE__ */ jsx4(
1084
+ "button",
1085
+ {
1086
+ type: "button",
1087
+ onClick: () => onPinOffering(model, void 0),
1088
+ title: "Auto \u2014 the server picks the preferred Service for each call",
1089
+ className: cn(
1090
+ "transition-colors",
1091
+ !anyPinnedHere ? SERVICE_CHIP_SELECTED : cn(
1092
+ SERVICE_CHIP_UNSELECTED,
1093
+ "hover:bg-muted/60 hover:text-foreground"
1094
+ )
1095
+ ),
1096
+ children: "Auto"
1097
+ }
1098
+ ),
1099
+ model.tiers.map((t) => {
1100
+ const pinned = pinnedHere(t);
1101
+ return /* @__PURE__ */ jsx4(
1102
+ "button",
1103
+ {
1104
+ type: "button",
1105
+ onClick: () => onPinOffering(
1106
+ model,
1107
+ pinned ? void 0 : t.offeringId
1108
+ ),
1109
+ title: pinned ? "Pinned \u2014 every call uses this Service. Click to return to Auto." : `Pin every call to ${t.servedVia}`,
1110
+ className: cn(
1111
+ "transition-colors",
1112
+ pinned ? SERVICE_CHIP_SELECTED : cn(
1113
+ SERVICE_CHIP_UNSELECTED,
1114
+ "hover:bg-muted/60 hover:text-foreground"
1115
+ )
1116
+ ),
1117
+ children: t.servedVia
1118
+ },
1119
+ t.offeringId
1120
+ );
1121
+ })
1122
+ ] }) : model.tiers.map((t, i) => /* @__PURE__ */ jsx4(
1123
+ "span",
1124
+ {
1125
+ className: i === 0 ? SERVICE_CHIP_SELECTED : SERVICE_CHIP_UNSELECTED,
1126
+ title: i === 0 ? "Default serving tier" : "Also available on this tier",
1127
+ children: t.servedVia
1128
+ },
1129
+ t.offeringId
1130
+ )) })
1131
+ ] }),
1132
+ model.multilingual && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 text-foreground/80", children: [
1133
+ /* @__PURE__ */ jsx4(LanguagesIcon, { className: "h-3.5 w-3.5" }),
1134
+ /* @__PURE__ */ jsx4("span", { children: "Multilingual" })
1135
+ ] }),
1136
+ model.features.raw.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
1137
+ /* @__PURE__ */ jsx4("dt", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "All capability flags" }),
1138
+ /* @__PURE__ */ jsx4("dd", { className: "font-mono text-[10px] leading-relaxed text-muted-foreground/80", children: model.features.raw.join(", ") })
1139
+ ] })
1140
+ ] }),
1141
+ includeAdminSection && variant === "admin" && /* @__PURE__ */ jsx4("div", { className: "mt-3 rounded-md border border-border", children: /* @__PURE__ */ jsx4(AdminOfferingsSection, { model }) })
1142
+ ] }),
1143
+ /* @__PURE__ */ jsx4("div", { className: "shrink-0 border-t border-border p-3", children: /* @__PURE__ */ jsx4(
1144
+ "button",
367
1145
  {
368
- id: inputId,
369
- value,
370
- onChange,
371
- disabled
1146
+ type: "button",
1147
+ onClick: onSelect,
1148
+ className: "w-full rounded-md bg-primary py-1.5 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90",
1149
+ children: "Select model"
372
1150
  }
373
- );
374
- }
375
- return /* @__PURE__ */ jsx(
376
- Input,
1151
+ ) })
1152
+ ] });
1153
+ }
1154
+ var HOVER_DELAY_MS = 400;
1155
+ function DelayedHint({
1156
+ label,
1157
+ children
1158
+ }) {
1159
+ return /* @__PURE__ */ jsxs3(Tooltip, { children: [
1160
+ /* @__PURE__ */ jsx4(TooltipTrigger, { asChild: true, children }),
1161
+ /* @__PURE__ */ jsx4(TooltipContent, { side: "top", sideOffset: 6, className: "max-w-64 text-xs", children: label })
1162
+ ] });
1163
+ }
1164
+ function ChipRow({ children }) {
1165
+ return /* @__PURE__ */ jsx4("div", { className: "flex flex-nowrap gap-1 overflow-x-auto pb-0.5", children });
1166
+ }
1167
+ function ChipToggle({
1168
+ active,
1169
+ onClick,
1170
+ children,
1171
+ className
1172
+ }) {
1173
+ return /* @__PURE__ */ jsx4(
1174
+ "button",
377
1175
  {
378
- id: inputId,
379
- type: "text",
380
- value: value === void 0 || value === null ? "" : String(value),
381
- onChange: (e) => onChange(e.target.value),
382
- disabled,
383
- className: "h-7 w-full px-2 text-base pointer-coarse:h-11 sm:text-xs"
1176
+ type: "button",
1177
+ onClick,
1178
+ className: cn(
1179
+ "inline-flex shrink-0 items-center rounded-md border px-2 py-0.5 text-[11px] transition-colors",
1180
+ active ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground",
1181
+ className
1182
+ ),
1183
+ children
384
1184
  }
385
1185
  );
386
1186
  }
387
- function JsonValueInput({
388
- id,
1187
+ function MakerFilterButton({
1188
+ maker,
1189
+ active,
1190
+ onClick
1191
+ }) {
1192
+ return /* @__PURE__ */ jsxs3(
1193
+ "button",
1194
+ {
1195
+ type: "button",
1196
+ "aria-pressed": active,
1197
+ "aria-label": `Filter by ${maker}`,
1198
+ onClick,
1199
+ className: cn(
1200
+ "flex min-w-0 items-center gap-1.5 rounded-md border px-1.5 py-1 text-left transition-colors",
1201
+ active ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground"
1202
+ ),
1203
+ children: [
1204
+ /* @__PURE__ */ jsx4(MakerBrandGlyph, { maker, colored: true, className: "h-4 w-4 shrink-0" }),
1205
+ /* @__PURE__ */ jsx4(DelayedHint, { label: maker, children: /* @__PURE__ */ jsx4("span", { className: "min-w-0 truncate text-[11px] font-medium leading-tight", children: maker }) })
1206
+ ]
1207
+ }
1208
+ );
1209
+ }
1210
+ function ServiceFilterButton({
1211
+ service,
1212
+ active,
1213
+ onClick
1214
+ }) {
1215
+ return /* @__PURE__ */ jsxs3(
1216
+ "button",
1217
+ {
1218
+ type: "button",
1219
+ "aria-pressed": active,
1220
+ "aria-label": `Filter by ${service}`,
1221
+ onClick,
1222
+ className: cn(
1223
+ "flex min-w-0 items-center gap-1.5 rounded-md border px-1.5 py-1 text-left transition-colors",
1224
+ active ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground"
1225
+ ),
1226
+ children: [
1227
+ /* @__PURE__ */ jsx4(ServiceBrandGlyph, { service, className: "h-4 w-4 shrink-0" }),
1228
+ /* @__PURE__ */ jsx4(DelayedHint, { label: service, children: /* @__PURE__ */ jsx4("span", { className: "min-w-0 truncate text-[11px] font-medium leading-tight", children: service }) })
1229
+ ]
1230
+ }
1231
+ );
1232
+ }
1233
+ function TriStateChips({
389
1234
  value,
390
1235
  onChange,
391
- disabled
1236
+ yesLabel,
1237
+ noLabel
392
1238
  }) {
393
- const toText = (v) => v === void 0 || v === null ? "" : JSON.stringify(v, null, 2);
394
- const [draft, setDraft] = useState(() => toText(value));
395
- const [jsonError, setJsonError] = useState(null);
396
- useEffect(() => {
397
- setDraft(toText(value));
398
- setJsonError(null);
399
- }, [JSON.stringify(value)]);
400
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
401
- /* @__PURE__ */ jsx(
402
- Textarea,
1239
+ return /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1", children: [
1240
+ ["any", "Any"],
1241
+ ["yes", yesLabel],
1242
+ ["no", noLabel]
1243
+ ].map(([key, label]) => /* @__PURE__ */ jsx4(
1244
+ ChipToggle,
1245
+ {
1246
+ active: value === key,
1247
+ onClick: () => onChange(key),
1248
+ children: label
1249
+ },
1250
+ key
1251
+ )) });
1252
+ }
1253
+ function FilterSection({
1254
+ label,
1255
+ children
1256
+ }) {
1257
+ return /* @__PURE__ */ jsxs3("div", { children: [
1258
+ /* @__PURE__ */ jsx4("div", { className: "mb-1 text-[10px] uppercase tracking-wide text-muted-foreground", children: label }),
1259
+ children
1260
+ ] });
1261
+ }
1262
+ function FiltersPanel({
1263
+ filters,
1264
+ setFilters,
1265
+ variant,
1266
+ makerOptions,
1267
+ serviceOptions,
1268
+ vendorOptions,
1269
+ apiOptions
1270
+ }) {
1271
+ const toggleModality = (kind, m) => setFilters((f) => {
1272
+ const next = new Set(f[kind]);
1273
+ if (next.has(m)) next.delete(m);
1274
+ else next.add(m);
1275
+ return { ...f, [kind]: next };
1276
+ });
1277
+ const toggleFeature = (b) => setFilters((f) => {
1278
+ const next = new Set(f.features);
1279
+ if (next.has(b)) next.delete(b);
1280
+ else next.add(b);
1281
+ return { ...f, features: next };
1282
+ });
1283
+ const toggleSetValue = (kind, value) => setFilters((f) => {
1284
+ const next = new Set(f[kind]);
1285
+ if (next.has(value)) next.delete(value);
1286
+ else next.add(value);
1287
+ return { ...f, [kind]: next };
1288
+ });
1289
+ const sortOptions = [
1290
+ { key: "name", label: "Name" },
1291
+ { key: "maker", label: "Maker" },
1292
+ { key: "service", label: SERVICE_LABEL },
1293
+ { key: "context", label: "Context window" },
1294
+ { key: "price", label: variant === "admin" ? "Price ($/M out)" : "Cost" },
1295
+ ...variant === "admin" ? [
1296
+ { key: "vendor", label: "Vendor" },
1297
+ { key: "api", label: "API" },
1298
+ { key: "deprecated", label: "Deprecated" },
1299
+ { key: "availability", label: "Availability" },
1300
+ { key: "premium", label: "Premium" }
1301
+ ] : []
1302
+ ];
1303
+ const setChip = (kind) => /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap gap-1", children: [
1304
+ /* @__PURE__ */ jsx4(
1305
+ ChipToggle,
403
1306
  {
404
- id,
405
- value: draft,
406
- onChange: (e) => {
407
- setDraft(e.target.value);
408
- try {
409
- JSON.parse(e.target.value);
410
- setJsonError(null);
411
- } catch (err) {
412
- setJsonError(err instanceof Error ? err.message : "Invalid JSON");
413
- }
1307
+ active: filters[kind].size === 0,
1308
+ onClick: () => setFilters((f) => ({ ...f, [kind]: /* @__PURE__ */ new Set() })),
1309
+ children: "Any"
1310
+ }
1311
+ ),
1312
+ (kind === "makers" ? makerOptions : kind === "services" ? serviceOptions : kind === "vendors" ? vendorOptions : apiOptions).map((v) => /* @__PURE__ */ jsx4(
1313
+ ChipToggle,
1314
+ {
1315
+ active: filters[kind].has(v),
1316
+ onClick: () => toggleSetValue(kind, v),
1317
+ children: v
1318
+ },
1319
+ v
1320
+ ))
1321
+ ] });
1322
+ return /* @__PURE__ */ jsxs3("div", { className: "flex h-full flex-col text-xs", children: [
1323
+ /* @__PURE__ */ jsxs3("div", { className: "min-h-0 flex-1 space-y-3 overflow-y-auto p-3 pb-4", children: [
1324
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Sort by", children: /* @__PURE__ */ jsx4(ChipRow, { children: sortOptions.map((o) => /* @__PURE__ */ jsx4(
1325
+ ChipToggle,
1326
+ {
1327
+ active: filters.sort === o.key,
1328
+ onClick: () => setFilters((f) => ({ ...f, sort: o.key })),
1329
+ children: o.label
414
1330
  },
415
- onBlur: (e) => {
416
- if (e.target.value.trim() === "") return;
417
- try {
418
- onChange(JSON.parse(e.target.value));
419
- setJsonError(null);
420
- } catch {
1331
+ o.key
1332
+ )) }) }),
1333
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Input", children: /* @__PURE__ */ jsxs3(ChipRow, { children: [
1334
+ /* @__PURE__ */ jsx4(
1335
+ ChipToggle,
1336
+ {
1337
+ active: filters.input.size === 0,
1338
+ onClick: () => setFilters((f) => ({ ...f, input: /* @__PURE__ */ new Set() })),
1339
+ children: "Any"
1340
+ }
1341
+ ),
1342
+ INPUT_MODALITIES.map((m) => /* @__PURE__ */ jsx4(
1343
+ ChipToggle,
1344
+ {
1345
+ active: filters.input.has(m),
1346
+ onClick: () => toggleModality("input", m),
1347
+ children: /* @__PURE__ */ jsx4("span", { className: "capitalize", children: m })
1348
+ },
1349
+ m
1350
+ ))
1351
+ ] }) }),
1352
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Output", children: /* @__PURE__ */ jsxs3(ChipRow, { children: [
1353
+ /* @__PURE__ */ jsx4(
1354
+ ChipToggle,
1355
+ {
1356
+ active: filters.output.size === 0,
1357
+ onClick: () => setFilters((f) => ({ ...f, output: /* @__PURE__ */ new Set() })),
1358
+ children: "Any"
1359
+ }
1360
+ ),
1361
+ OUTPUT_MODALITIES.map((m) => /* @__PURE__ */ jsx4(
1362
+ ChipToggle,
1363
+ {
1364
+ active: filters.output.has(m),
1365
+ onClick: () => toggleModality("output", m),
1366
+ children: /* @__PURE__ */ jsx4("span", { className: "capitalize", children: m })
1367
+ },
1368
+ m
1369
+ ))
1370
+ ] }) }),
1371
+ variant === "admin" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1372
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Deprecated", children: /* @__PURE__ */ jsx4(
1373
+ TriStateChips,
1374
+ {
1375
+ value: filters.deprecated,
1376
+ onChange: (v) => setFilters((f) => ({ ...f, deprecated: v })),
1377
+ yesLabel: "Deprecated only",
1378
+ noLabel: "Active only"
1379
+ }
1380
+ ) }),
1381
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Availability", children: /* @__PURE__ */ jsx4(
1382
+ TriStateChips,
1383
+ {
1384
+ value: filters.availability,
1385
+ onChange: (v) => setFilters((f) => ({ ...f, availability: v })),
1386
+ yesLabel: "Available",
1387
+ noLabel: "Unavailable"
1388
+ }
1389
+ ) }),
1390
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Premium", children: /* @__PURE__ */ jsx4(
1391
+ TriStateChips,
1392
+ {
1393
+ value: filters.premium,
1394
+ onChange: (v) => setFilters((f) => ({ ...f, premium: v })),
1395
+ yesLabel: "Premium only",
1396
+ noLabel: "Standard only"
1397
+ }
1398
+ ) }),
1399
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Vendor", children: setChip("vendors") }),
1400
+ /* @__PURE__ */ jsx4(FilterSection, { label: "API", children: setChip("apis") })
1401
+ ] }),
1402
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Maker", children: /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-3 gap-1", children: [
1403
+ /* @__PURE__ */ jsx4(
1404
+ ChipToggle,
1405
+ {
1406
+ active: filters.makers.size === 0,
1407
+ onClick: () => setFilters((f) => ({ ...f, makers: /* @__PURE__ */ new Set() })),
1408
+ className: "justify-center py-1",
1409
+ children: "Any"
1410
+ }
1411
+ ),
1412
+ makerOptions.map((maker) => /* @__PURE__ */ jsx4(
1413
+ MakerFilterButton,
1414
+ {
1415
+ maker,
1416
+ active: filters.makers.has(maker),
1417
+ onClick: () => toggleSetValue("makers", maker)
1418
+ },
1419
+ maker
1420
+ ))
1421
+ ] }) }),
1422
+ /* @__PURE__ */ jsx4(FilterSection, { label: SERVICE_LABEL, children: /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-3 gap-1", children: [
1423
+ /* @__PURE__ */ jsx4(
1424
+ ChipToggle,
1425
+ {
1426
+ active: filters.services.size === 0,
1427
+ onClick: () => setFilters((f) => ({ ...f, services: /* @__PURE__ */ new Set() })),
1428
+ className: "justify-center py-1",
1429
+ children: "Any"
421
1430
  }
1431
+ ),
1432
+ serviceOptions.map((service) => /* @__PURE__ */ jsx4(
1433
+ ServiceFilterButton,
1434
+ {
1435
+ service,
1436
+ active: filters.services.has(service),
1437
+ onClick: () => toggleSetValue("services", service)
1438
+ },
1439
+ service
1440
+ ))
1441
+ ] }) }),
1442
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Features", children: /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1", children: FEATURE_BUCKET_ORDER.map((b) => /* @__PURE__ */ jsx4(
1443
+ ChipToggle,
1444
+ {
1445
+ active: filters.features.has(b),
1446
+ onClick: () => toggleFeature(b),
1447
+ children: FEATURE_BUCKETS[b].label
422
1448
  },
423
- disabled,
424
- className: "min-h-12 px-2 py-1.5 font-mono text-base leading-snug sm:text-xs",
425
- spellCheck: false
426
- }
427
- ),
428
- jsonError && /* @__PURE__ */ jsx("p", { className: "type-meta text-amber-600 dark:text-amber-400", children: jsonError })
1449
+ b
1450
+ )) }) }),
1451
+ /* @__PURE__ */ jsx4(FilterSection, { label: "Interaction", children: /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1", children: ["any", "turn", "single", "extraction", "realtime"].map(
1452
+ (i) => /* @__PURE__ */ jsx4(
1453
+ ChipToggle,
1454
+ {
1455
+ active: filters.interaction === i,
1456
+ onClick: () => setFilters((f) => ({ ...f, interaction: i })),
1457
+ children: i === "any" ? "Any" : INTERACTION_LABEL[i]
1458
+ },
1459
+ i
1460
+ )
1461
+ ) }) })
1462
+ ] }),
1463
+ /* @__PURE__ */ jsxs3("div", { className: "z-10 flex shrink-0 flex-nowrap items-center gap-1 border-t border-border bg-popover px-3 py-2", children: [
1464
+ /* @__PURE__ */ jsx4(
1465
+ ChipToggle,
1466
+ {
1467
+ active: filters.multilingualOnly,
1468
+ onClick: () => setFilters((f) => ({ ...f, multilingualOnly: !f.multilingualOnly })),
1469
+ className: "flex-1 justify-center",
1470
+ children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1", children: [
1471
+ /* @__PURE__ */ jsx4(LanguagesIcon, { className: "h-3 w-3" }),
1472
+ " Multilingual"
1473
+ ] })
1474
+ }
1475
+ ),
1476
+ variant === "user" && /* @__PURE__ */ jsx4(
1477
+ ChipToggle,
1478
+ {
1479
+ active: filters.includeDeprecated,
1480
+ onClick: () => setFilters((f) => ({
1481
+ ...f,
1482
+ includeDeprecated: !f.includeDeprecated
1483
+ })),
1484
+ className: "flex-1 justify-center",
1485
+ children: "Include deprecated"
1486
+ }
1487
+ )
1488
+ ] })
429
1489
  ] });
430
1490
  }
431
- function NumberInput({
432
- value,
433
- placeholderValue,
434
- onChange,
435
- onSliderChange,
436
- min,
437
- max,
438
- step = 1,
439
- isInteger = false,
440
- disabled = false,
441
- withSlider = false
1491
+ function ModelRow({
1492
+ model,
1493
+ classTier,
1494
+ tier,
1495
+ variant,
1496
+ selected,
1497
+ isFavorite,
1498
+ locked = false,
1499
+ onSelect,
1500
+ onHover,
1501
+ onToggleFavorite
442
1502
  }) {
443
- const boxClass = "h-7 px-2 text-xs tabular-nums pointer-coarse:h-11";
444
- const [draftState, setDraftState] = useState(() => ({
445
- sourceValue: value,
446
- text: value === void 0 ? "" : String(value)
447
- }));
448
- const shown = value === void 0 ? "" : String(value);
449
- const draft = draftState.sourceValue === value ? draftState.text : shown;
450
- const unset = value === void 0;
451
- const hint = placeholderValue === void 0 ? "Default" : String(placeholderValue);
452
- const setDraft = (text) => setDraftState({ sourceValue: value, text });
453
- const sliderStep = isInteger ? Math.max(1, Math.trunc(step)) : step;
454
- const commit = (raw) => {
455
- if (raw === "" || raw === "-") return;
456
- const parsed = isInteger ? parseInt(raw, 10) : parseFloat(raw);
457
- if (!isNaN(parsed)) {
458
- if (parsed !== value) onChange(parsed);
459
- } else setDraft(shown);
460
- };
461
- if (withSlider) {
462
- return /* @__PURE__ */ jsxs(
463
- "div",
464
- {
465
- className: "grid grid-cols-[1fr_4.5rem] items-center gap-2",
466
- children: [
467
- /* @__PURE__ */ jsx(
468
- Slider,
469
- {
470
- ...min !== void 0 ? { min } : {},
471
- ...max !== void 0 ? { max } : {},
472
- step: sliderStep,
473
- value: [value ?? placeholderValue ?? min ?? 0],
474
- "data-unset": unset ? "true" : void 0,
475
- className: unset ? "opacity-50" : void 0,
476
- onValueChange: (val) => {
477
- const first = val[0] ?? value ?? placeholderValue ?? min ?? 0;
478
- const nextValue = isInteger ? Math.trunc(first) : first;
479
- onSliderChange?.(nextValue);
480
- setDraft(String(nextValue));
481
- },
482
- disabled
1503
+ const unavailable = variant === "admin" && !(model.admin?.offerings ?? []).some((o) => o.isAvailable);
1504
+ const retired = model.retiredAt != null;
1505
+ const retiredTitle = retired ? `Retired ${model.retiredAt?.slice(0, 10)} \u2014 the provider no longer serves this model, so it cannot be selected.` : locked ? TOP_TIER_LOCKED_TITLE : void 0;
1506
+ const blocked = retired || locked;
1507
+ return /* @__PURE__ */ jsxs3(
1508
+ "div",
1509
+ {
1510
+ role: "button",
1511
+ tabIndex: 0,
1512
+ "aria-disabled": blocked || void 0,
1513
+ title: retiredTitle,
1514
+ onClick: blocked ? void 0 : onSelect,
1515
+ onKeyDown: (e) => {
1516
+ if (blocked) return;
1517
+ if (e.key === "Enter" || e.key === " ") {
1518
+ e.preventDefault();
1519
+ onSelect();
1520
+ }
1521
+ },
1522
+ onMouseEnter: onHover,
1523
+ className: cn(
1524
+ "grid w-full grid-cols-[16px_20px_minmax(0,1fr)_2.75rem_3rem_2.75rem] items-center gap-2 rounded px-2 py-1 transition-colors",
1525
+ blocked ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:bg-muted/60 focus:bg-muted/60 focus:outline-none",
1526
+ selected && "bg-muted"
1527
+ ),
1528
+ children: [
1529
+ /* @__PURE__ */ jsx4(
1530
+ "button",
1531
+ {
1532
+ type: "button",
1533
+ onClick: (e) => {
1534
+ e.stopPropagation();
1535
+ onToggleFavorite();
1536
+ },
1537
+ title: isFavorite ? "Remove favorite" : "Add favorite",
1538
+ "aria-label": isFavorite ? "Remove favorite" : "Add favorite",
1539
+ className: cn(
1540
+ "flex h-4 w-4 items-center justify-center rounded transition-colors",
1541
+ isFavorite ? "text-amber-400" : "text-muted-foreground/30 hover:text-muted-foreground"
1542
+ ),
1543
+ children: /* @__PURE__ */ jsx4(StarIcon, { className: cn("h-3 w-3", isFavorite && "fill-amber-400") })
1544
+ }
1545
+ ),
1546
+ /* @__PURE__ */ jsx4(
1547
+ "span",
1548
+ {
1549
+ className: "flex w-5 justify-center",
1550
+ title: model.maker ?? void 0,
1551
+ children: /* @__PURE__ */ jsx4(MakerBrandGlyph, { maker: model.maker, colored: true, className: "h-3.5 w-3.5" })
1552
+ }
1553
+ ),
1554
+ /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 items-center gap-1.5", children: [
1555
+ /* @__PURE__ */ jsx4(DelayedHint, { label: model.name, children: /* @__PURE__ */ jsx4("span", { className: "truncate text-xs font-medium text-foreground", children: model.name }) }),
1556
+ isDecisionModelCapability(model) && /* @__PURE__ */ jsx4(
1557
+ "span",
1558
+ {
1559
+ className: "shrink-0 rounded border border-primary/40 bg-primary/10 px-1 text-[9px] font-medium text-foreground/80",
1560
+ title: "Decision model \u2014 it answers a Questions part with probabilities and generates no text.",
1561
+ children: "decision"
483
1562
  }
484
1563
  ),
485
- /* @__PURE__ */ jsx(
486
- Input,
1564
+ model.isDeprecated && !retired && /* @__PURE__ */ jsx4(
1565
+ "span",
487
1566
  {
488
- type: "text",
489
- inputMode: "decimal",
490
- value: draft,
491
- placeholder: hint,
492
- onChange: (e) => setDraft(e.target.value),
493
- onBlur: (e) => commit(e.target.value),
494
- onFocus: (e) => e.target.select(),
495
- onKeyDown: (e) => {
496
- if (e.key === "Enter") e.currentTarget.blur();
497
- },
498
- disabled,
499
- className: boxClass
1567
+ className: "shrink-0 rounded bg-destructive/15 px-1 text-[9px] text-destructive-ink",
1568
+ title: "Deprecated \u2014 still runs; hidden from default selection",
1569
+ children: "dep"
1570
+ }
1571
+ ),
1572
+ retired && /* @__PURE__ */ jsx4(
1573
+ "span",
1574
+ {
1575
+ className: "shrink-0 rounded bg-destructive/15 px-1 text-[9px] text-destructive-ink",
1576
+ title: retiredTitle,
1577
+ children: "retired"
1578
+ }
1579
+ ),
1580
+ unavailable && /* @__PURE__ */ jsx4(
1581
+ "span",
1582
+ {
1583
+ className: "shrink-0 rounded bg-destructive/15 px-1 text-[9px] text-destructive-ink",
1584
+ title: "No available offerings \u2014 not callable",
1585
+ children: "off"
1586
+ }
1587
+ ),
1588
+ classTier ? /* @__PURE__ */ jsx4(
1589
+ "span",
1590
+ {
1591
+ className: "shrink-0 rounded border border-primary/40 bg-primary/10 px-1 text-[9px] font-medium text-foreground/80",
1592
+ title: `Runs on ${classTier.servedVia}`,
1593
+ children: classTier.servedVia
1594
+ }
1595
+ ) : model.tiers.length > 1 && /* @__PURE__ */ jsx4(
1596
+ "span",
1597
+ {
1598
+ className: "shrink-0 rounded border border-primary/40 bg-primary/10 px-1 text-[9px] font-medium text-foreground/80",
1599
+ title: model.tiers.map((t) => t.servedVia).join(" \xB7 "),
1600
+ children: serviceCountLabel(model.tiers.length)
500
1601
  }
501
1602
  )
502
- ]
503
- }
504
- );
505
- }
506
- return /* @__PURE__ */ jsx(
507
- Input,
1603
+ ] }),
1604
+ /* @__PURE__ */ jsx4(SpeedCell, { value: model.speedRating }),
1605
+ /* @__PURE__ */ jsx4(
1606
+ DelayedHint,
1607
+ {
1608
+ label: model.contextWindow == null ? "No context window listed" : `Context window ${formatCount(model.contextWindow)} tokens`,
1609
+ children: /* @__PURE__ */ jsx4("span", { className: "block w-12 whitespace-nowrap text-right font-mono text-[11px] tabular-nums text-foreground/80", children: model.contextWindow == null ? "\u2014" : fmtPointsCompact(model.contextWindow) })
1610
+ }
1611
+ ),
1612
+ /* @__PURE__ */ jsx4(
1613
+ DelayedHint,
1614
+ {
1615
+ label: tier == null ? "No relative cost rating yet" : `Relative cost ${tier.replace("+", "")}${tier.endsWith("+") ? "+" : ""} \u2014 more $ is more expensive`,
1616
+ children: /* @__PURE__ */ jsx4("span", { className: "block w-[2.75rem] text-right", children: /* @__PURE__ */ jsx4(CostTier, { tier }) })
1617
+ }
1618
+ )
1619
+ ]
1620
+ }
1621
+ );
1622
+ }
1623
+ function LocalModelRow({ model, selected, onSelect }) {
1624
+ const dim = model.needsMoreMemory === true;
1625
+ return /* @__PURE__ */ jsxs3(
1626
+ "div",
508
1627
  {
509
- type: "text",
510
- inputMode: "decimal",
511
- value: draft,
512
- placeholder: hint,
513
- onChange: (e) => setDraft(e.target.value),
514
- onBlur: (e) => commit(e.target.value),
515
- onFocus: (e) => e.target.select(),
1628
+ role: "button",
1629
+ tabIndex: 0,
1630
+ "data-local-model": model.id,
1631
+ title: dim ? "Needs more memory than this computer has free" : void 0,
1632
+ onClick: onSelect,
516
1633
  onKeyDown: (e) => {
517
- if (e.key === "Enter") e.currentTarget.blur();
1634
+ if (e.key === "Enter" || e.key === " ") {
1635
+ e.preventDefault();
1636
+ onSelect();
1637
+ }
518
1638
  },
1639
+ className: cn(
1640
+ "grid w-full cursor-pointer grid-cols-[16px_20px_minmax(0,1fr)_2.75rem_3rem_2.75rem] items-center gap-2 rounded px-2 py-1 transition-colors hover:bg-muted/60 focus:bg-muted/60 focus:outline-none",
1641
+ dim && "opacity-60",
1642
+ selected && "bg-muted"
1643
+ ),
1644
+ children: [
1645
+ /* @__PURE__ */ jsx4("span", { className: "w-4" }),
1646
+ /* @__PURE__ */ jsx4("span", { className: "flex w-5 justify-center", title: model.maker ?? void 0, children: /* @__PURE__ */ jsx4(MakerBrandGlyph, { maker: model.maker ?? null, colored: true, className: "h-3.5 w-3.5" }) }),
1647
+ /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 items-center gap-1.5", children: [
1648
+ /* @__PURE__ */ jsx4("span", { className: "truncate text-xs font-medium text-foreground", children: model.name }),
1649
+ /* @__PURE__ */ jsx4(
1650
+ "span",
1651
+ {
1652
+ className: "shrink-0 rounded border border-primary/40 bg-primary/10 px-1 text-[9px] font-medium text-foreground/80",
1653
+ title: "Runs on this computer",
1654
+ children: "local"
1655
+ }
1656
+ ),
1657
+ dim && /* @__PURE__ */ jsx4("span", { className: "shrink-0 rounded bg-muted px-1 text-[9px] text-muted-foreground", title: "Needs more memory than this computer has free", children: "needs memory" })
1658
+ ] }),
1659
+ /* @__PURE__ */ jsx4("span", { className: "w-11" }),
1660
+ /* @__PURE__ */ jsx4("span", { className: "block w-12 whitespace-nowrap text-right font-mono text-[11px] tabular-nums text-foreground/80", children: model.contextWindow == null ? "\u2014" : fmtPointsCompact(model.contextWindow) }),
1661
+ /* @__PURE__ */ jsx4("span", { className: "w-[2.75rem]" })
1662
+ ]
1663
+ }
1664
+ );
1665
+ }
1666
+ var GROUP_HEADING = "px-2 pb-0.5 pt-1.5 text-[10px] uppercase tracking-wide text-muted-foreground";
1667
+ function ModelListDropdown({
1668
+ value: savedValue,
1669
+ localModels: localModelsProp,
1670
+ onValueChange,
1671
+ inputModalities,
1672
+ outputModalities,
1673
+ selectionPurpose,
1674
+ allowedModelIds,
1675
+ catalogVariant,
1676
+ emptyOptionLabel,
1677
+ onClear,
1678
+ placeholder = "Select a model\u2026",
1679
+ priorityModelIds,
1680
+ pinnedOfferingId,
1681
+ onOfferingPinChange,
1682
+ modelOnly,
1683
+ className,
1684
+ triggerVariant = "default",
1685
+ presentation = "picker",
1686
+ id,
1687
+ disabled = false,
1688
+ disabledTitle,
1689
+ "aria-label": ariaLabel,
1690
+ "aria-labelledby": ariaLabelledBy
1691
+ }) {
1692
+ const isMobile = useIsMobile();
1693
+ const dialogContainer = useDialogContainer();
1694
+ const [pickerOpen, setOpen] = useState(false);
1695
+ const inline = presentation === "inline";
1696
+ const open = inline || pickerOpen;
1697
+ const { favoriteSet, toggleFavorite } = useModelFavorites(open);
1698
+ const ports = useModelCatalogUiPorts();
1699
+ const canUseTopTier = ports.canUseTopTier ?? false;
1700
+ const isSuperAdmin = ports.isSuperAdmin ?? false;
1701
+ const LinkComponent = ports.LinkComponent;
1702
+ const hiddenModelIds = ports.hiddenModelIds;
1703
+ const [adminMode, setAdminMode] = useState(false);
1704
+ const variant = catalogVariant ?? (adminMode && isSuperAdmin ? "admin" : "user");
1705
+ const effectiveSelectionPurpose = selectionPurpose ?? (variant === "admin" ? "admin" : "chat");
1706
+ const { models, isLoading, error } = useModelCatalog(variant);
1707
+ const successionIndex = useMemo2(() => buildSuccessionIndex(models), [models]);
1708
+ const value = savedValue ? resolveSuccessor(savedValue, successionIndex) : savedValue;
1709
+ const healedFrom = useRef(null);
1710
+ useEffect2(() => {
1711
+ if (!savedValue || !value || value === savedValue || healedFrom.current === savedValue) return;
1712
+ healedFrom.current = savedValue;
1713
+ if (onOfferingPinChange && pinnedOfferingId) onOfferingPinChange(void 0);
1714
+ onValueChange(value);
1715
+ }, [savedValue, value, onOfferingPinChange, pinnedOfferingId, onValueChange]);
1716
+ const [query, setQuery] = useState("");
1717
+ const [tab, setTab] = useState("all");
1718
+ const [rightPanel, setRightPanel] = useState(null);
1719
+ const [hovered, setHovered] = useState(null);
1720
+ const [hoveredClass, setHoveredClass] = useState(null);
1721
+ const [mobileDetail, setMobileDetail] = useState(null);
1722
+ const [mobileDetailClass, setMobileDetailClass] = useState(null);
1723
+ const [mobileFilters, setMobileFilters] = useState(false);
1724
+ const inputRef = useRef(null);
1725
+ const [filters, setFilters] = useState(() => ({
1726
+ input: new Set(inputModalities),
1727
+ output: new Set(outputModalities ?? []),
1728
+ features: /* @__PURE__ */ new Set(),
1729
+ makers: /* @__PURE__ */ new Set(),
1730
+ services: /* @__PURE__ */ new Set(),
1731
+ interaction: "any",
1732
+ multilingualOnly: false,
1733
+ sort: "name",
1734
+ vendors: /* @__PURE__ */ new Set(),
1735
+ apis: /* @__PURE__ */ new Set(),
1736
+ includeDeprecated: false,
1737
+ deprecated: "any",
1738
+ availability: "any",
1739
+ premium: "any"
1740
+ }));
1741
+ const setVariantMode = (nextAdmin) => {
1742
+ setAdminMode(nextAdmin);
1743
+ setHovered(null);
1744
+ if (!nextAdmin) {
1745
+ setFilters((f) => ({
1746
+ ...f,
1747
+ vendors: /* @__PURE__ */ new Set(),
1748
+ apis: /* @__PURE__ */ new Set(),
1749
+ deprecated: "any",
1750
+ availability: "any",
1751
+ premium: "any",
1752
+ sort: ADMIN_ONLY_SORTS.has(f.sort) ? "name" : f.sort
1753
+ }));
1754
+ }
1755
+ };
1756
+ const allowedModelSet = allowedModelIds ? new Set(allowedModelIds) : null;
1757
+ const priorityModelSet = priorityModelIds ? new Set(priorityModelIds) : null;
1758
+ const eligibleModels = allowedModelSet ? models.filter((model) => allowedModelSet.has(model.id)) : models;
1759
+ const selected = models.find((m) => m.id === value) ?? null;
1760
+ const { models: localAll, groupLabel: localGroupLabel, open: openLocal } = useLocalModels(localModelsProp);
1761
+ const selectedLocal = localAll.find((m) => m.id === value) ?? null;
1762
+ const localEligible = !modelOnly && variant === "user" && !allowedModelSet && (effectiveSelectionPurpose === "chat" || effectiveSelectionPurpose === "agent");
1763
+ const localRows = useMemo2(() => {
1764
+ if (!localEligible || tab !== "all") return [];
1765
+ const q = query.trim().toLowerCase();
1766
+ return q ? localAll.filter((m) => [m.name, m.maker, "local"].some((f) => f?.toLowerCase().includes(q))) : localAll;
1767
+ }, [localEligible, tab, query, localAll]);
1768
+ const shown = selected ?? (selectedLocal ? { name: selectedLocal.name, maker: selectedLocal.maker ?? null } : null);
1769
+ const makerOptions = useMemo2(
1770
+ () => [
1771
+ ...new Set(
1772
+ eligibleModels.map((m) => m.maker).filter((v) => !!v)
1773
+ )
1774
+ ].sort((a, b) => a.localeCompare(b)),
1775
+ [eligibleModels]
1776
+ );
1777
+ const serviceOptions = useMemo2(
1778
+ () => [
1779
+ ...new Set(
1780
+ eligibleModels.flatMap((m) => m.tiers.map((t) => t.servedVia)).filter(Boolean)
1781
+ )
1782
+ ].sort((a, b) => a.localeCompare(b)),
1783
+ [eligibleModels]
1784
+ );
1785
+ const vendorOptions = useMemo2(
1786
+ () => [
1787
+ ...new Set(
1788
+ eligibleModels.flatMap((m) => (m.admin?.offerings ?? []).map((o) => o.vendor)).filter((v) => !!v)
1789
+ )
1790
+ ].sort((a, b) => a.localeCompare(b)),
1791
+ [eligibleModels]
1792
+ );
1793
+ const apiOptions = useMemo2(
1794
+ () => [
1795
+ ...new Set(
1796
+ eligibleModels.flatMap((m) => (m.admin?.offerings ?? []).map((o) => o.apiName)).filter((v) => !!v)
1797
+ )
1798
+ ].sort((a, b) => a.localeCompare(b)),
1799
+ [eligibleModels]
1800
+ );
1801
+ const chatFitApplies = effectiveSelectionPurpose === "chat" && !(outputModalities ?? []).some((o) => o !== "text");
1802
+ const filtered = useMemo2(() => {
1803
+ const q = query.trim().toLowerCase();
1804
+ const hasAvailable = (m) => (m.admin?.offerings ?? []).some((o) => o.isAvailable);
1805
+ const rows = eligibleModels.filter((m) => {
1806
+ const isDecision = isDecisionModelCapability(m);
1807
+ if (effectiveSelectionPurpose === "chat" && isDecision) return false;
1808
+ if (effectiveSelectionPurpose === "decision" && !isDecision) return false;
1809
+ if (effectiveSelectionPurpose !== "admin" && !acceptsTextInstructions(m))
1810
+ return false;
1811
+ if (effectiveSelectionPurpose === "chat" && isManagedAgentModelCapability(m))
1812
+ return false;
1813
+ if (chatFitApplies && !isChatModelCapability(m)) return false;
1814
+ if (tab === "favorites" && !favoriteSet.has(m.id)) return false;
1815
+ const haystack = [
1816
+ m.name,
1817
+ m.maker,
1818
+ ...m.tiers.map((t) => t.servedVia),
1819
+ ...variant === "admin" ? (m.admin?.offerings ?? []).flatMap((o) => [
1820
+ o.vendor,
1821
+ o.apiName,
1822
+ o.providerModelId
1823
+ ]) : []
1824
+ ];
1825
+ if (q && !haystack.some((f) => f?.toLowerCase().includes(q)))
1826
+ return false;
1827
+ if (filters.makers.size > 0 && (!m.maker || !filters.makers.has(m.maker)))
1828
+ return false;
1829
+ if (filters.services.size > 0 && !m.tiers.some((t) => filters.services.has(t.servedVia)))
1830
+ return false;
1831
+ if (filters.vendors.size > 0 && !(m.admin?.offerings ?? []).some(
1832
+ (o) => o.vendor != null && filters.vendors.has(o.vendor)
1833
+ ))
1834
+ return false;
1835
+ if (filters.apis.size > 0 && !(m.admin?.offerings ?? []).some(
1836
+ (o) => o.apiName != null && filters.apis.has(o.apiName)
1837
+ ))
1838
+ return false;
1839
+ if (variant === "user" && m.id !== value && hiddenModelIds?.includes(m.id))
1840
+ return false;
1841
+ if (variant === "user" && !filters.includeDeprecated && m.isDeprecated && m.id !== value)
1842
+ return false;
1843
+ if (variant === "user" && resolveSuccessor(m.id, successionIndex) !== m.id) return false;
1844
+ if (filters.deprecated !== "any") {
1845
+ if (filters.deprecated === "yes" !== m.isDeprecated) return false;
1846
+ }
1847
+ if (filters.availability !== "any") {
1848
+ if (filters.availability === "yes" !== hasAvailable(m)) return false;
1849
+ }
1850
+ if (filters.premium !== "any") {
1851
+ if (filters.premium === "yes" !== m.isPremium) return false;
1852
+ }
1853
+ for (const m2 of filters.input) if (!takesInput(m, m2)) return false;
1854
+ const outputSatisfied = (want) => m.output.includes(want) || effectiveSelectionPurpose === "agent" && isDecision && want === "text";
1855
+ for (const m2 of filters.output) if (!outputSatisfied(m2)) return false;
1856
+ for (const b of filters.features)
1857
+ if (!m.features.buckets.includes(b)) return false;
1858
+ if (filters.interaction !== "any" && m.interaction !== filters.interaction)
1859
+ return false;
1860
+ if (filters.multilingualOnly && !m.multilingual) return false;
1861
+ return true;
1862
+ });
1863
+ const cmp = (a, b) => {
1864
+ switch (filters.sort) {
1865
+ case "maker":
1866
+ return (a.maker ?? "").localeCompare(b.maker ?? "");
1867
+ case "service":
1868
+ return (a.tiers[0]?.servedVia ?? "\uFFFF").localeCompare(
1869
+ b.tiers[0]?.servedVia ?? "\uFFFF"
1870
+ ) || b.tiers.length - a.tiers.length;
1871
+ case "context":
1872
+ return (b.contextWindow ?? 0) - (a.contextWindow ?? 0);
1873
+ case "price": {
1874
+ const price = (m) => (variant === "admin" ? m.outputCost : m.costRating) ?? Infinity;
1875
+ return price(a) - price(b) || a.name.localeCompare(b.name);
1876
+ }
1877
+ case "vendor":
1878
+ return (a.admin?.offerings[0]?.vendor ?? "\uFFFF").localeCompare(
1879
+ b.admin?.offerings[0]?.vendor ?? "\uFFFF"
1880
+ ) || a.name.localeCompare(b.name);
1881
+ case "api":
1882
+ return (a.admin?.offerings[0]?.apiName ?? "\uFFFF").localeCompare(
1883
+ b.admin?.offerings[0]?.apiName ?? "\uFFFF"
1884
+ ) || a.name.localeCompare(b.name);
1885
+ case "deprecated":
1886
+ return Number(a.isDeprecated) - Number(b.isDeprecated) || a.name.localeCompare(b.name);
1887
+ case "availability": {
1888
+ const avail = (m) => (m.admin?.offerings ?? []).filter((o) => o.isAvailable).length;
1889
+ return avail(b) - avail(a) || a.name.localeCompare(b.name);
1890
+ }
1891
+ case "premium":
1892
+ return Number(b.isPremium) - Number(a.isPremium) || a.name.localeCompare(b.name);
1893
+ default:
1894
+ return a.name.localeCompare(b.name);
1895
+ }
1896
+ };
1897
+ const sorted = [...rows].sort(cmp);
1898
+ const prioritize = (items) => {
1899
+ if (!priorityModelSet || priorityModelSet.size === 0) return items;
1900
+ const priority = items.filter((m) => priorityModelSet.has(m.id));
1901
+ const rest = items.filter((m) => !priorityModelSet.has(m.id));
1902
+ return [...priority, ...rest];
1903
+ };
1904
+ if (tab === "all") {
1905
+ const favs = sorted.filter((m) => favoriteSet.has(m.id));
1906
+ const rest = sorted.filter((m) => !favoriteSet.has(m.id));
1907
+ return [...prioritize(favs), ...prioritize(rest)];
1908
+ }
1909
+ return prioritize(sorted);
1910
+ }, [
1911
+ eligibleModels,
1912
+ query,
1913
+ filters,
1914
+ tab,
1915
+ favoriteSet,
1916
+ variant,
1917
+ priorityModelSet,
1918
+ effectiveSelectionPurpose,
1919
+ chatFitApplies,
1920
+ hiddenModelIds,
1921
+ value,
1922
+ successionIndex
1923
+ ]);
1924
+ const listRows = useMemo2(
1925
+ () => filtered.flatMap((m) => {
1926
+ if (modelOnly || m.tiers.length < 2) {
1927
+ return [{ model: m, classTier: null }];
1928
+ }
1929
+ const classes = filters.services.size > 0 ? m.tiers.filter((t) => filters.services.has(t.servedVia)) : m.tiers;
1930
+ return classes.map((t) => ({ model: m, classTier: t }));
1931
+ }),
1932
+ [filtered, modelOnly, filters.services]
1933
+ );
1934
+ const selectedClass = selected ? selected.tiers.find((t) => t.offeringId === pinnedOfferingId) ?? selected.tiers[0] ?? null : null;
1935
+ const selectedClassLabel = !modelOnly && selected && selected.tiers.length > 1 ? selectedClass?.servedVia : void 0;
1936
+ const activeFilterCount = filters.input.size + filters.output.size + filters.features.size + filters.makers.size + filters.services.size + filters.vendors.size + filters.apis.size + (filters.deprecated !== "any" ? 1 : 0) + (filters.availability !== "any" ? 1 : 0) + (filters.premium !== "any" ? 1 : 0) + (filters.interaction !== "any" ? 1 : 0) + (filters.multilingualOnly ? 1 : 0) + (filters.includeDeprecated ? 1 : 0);
1937
+ const handleOpen = (next) => {
1938
+ setOpen(next);
1939
+ if (next) {
1940
+ setTimeout(() => inputRef.current?.focus(), 50);
1941
+ } else {
1942
+ setRightPanel(null);
1943
+ setHovered(null);
1944
+ setMobileDetail(null);
1945
+ setMobileFilters(false);
1946
+ setQuery("");
1947
+ }
1948
+ };
1949
+ const refuseIfRetired = (id2) => {
1950
+ const target = models.find((m) => m.id === id2);
1951
+ if (target && !canUseTopTier && isTopTierRating(target.costRating)) {
1952
+ console.warn(`[ModelListDropdown] Refused to select ${target.name} (${id2}): ${TOP_TIER_LOCKED_TITLE}.`);
1953
+ return true;
1954
+ }
1955
+ if (!target?.retiredAt) return false;
1956
+ console.warn(
1957
+ `[ModelListDropdown] Refused to select ${target.name} (${id2}): retired ${target.retiredAt} \u2014 the provider no longer serves it.`
1958
+ );
1959
+ return true;
1960
+ };
1961
+ const handleSelectClass = (model, classTier) => {
1962
+ if (refuseIfRetired(model.id)) return;
1963
+ if (onOfferingPinChange) {
1964
+ const nextPin = classTier && model.tiers.length > 1 ? classTier.offeringId : void 0;
1965
+ if (nextPin !== (pinnedOfferingId ?? void 0)) onOfferingPinChange(nextPin);
1966
+ }
1967
+ onValueChange(model.id);
1968
+ handleOpen(false);
1969
+ };
1970
+ const handleSelect = (id2, tappedClass) => {
1971
+ const next = models.find((m) => m.id === id2);
1972
+ if (!next) return;
1973
+ const pinnedHere = next.tiers.find((t) => t.offeringId === pinnedOfferingId) ?? null;
1974
+ handleSelectClass(next, tappedClass ?? pinnedHere ?? next.tiers[0] ?? null);
1975
+ };
1976
+ const handleSelectLocal = (local) => {
1977
+ if (openLocal) {
1978
+ openLocal(local.id);
1979
+ handleOpen(false);
1980
+ return;
1981
+ }
1982
+ if (onOfferingPinChange && pinnedOfferingId) onOfferingPinChange(void 0);
1983
+ onValueChange(local.id);
1984
+ handleOpen(false);
1985
+ };
1986
+ const handleClear = () => {
1987
+ onClear?.();
1988
+ handleOpen(false);
1989
+ };
1990
+ const handlePinOffering = (model, offeringId) => {
1991
+ if (!onOfferingPinChange) return;
1992
+ if (refuseIfRetired(model.id)) return;
1993
+ onOfferingPinChange(offeringId);
1994
+ if (model.id !== value) onValueChange(model.id);
1995
+ };
1996
+ const trigger = /* @__PURE__ */ jsxs3(
1997
+ "button",
1998
+ {
1999
+ type: "button",
2000
+ id,
519
2001
  disabled,
520
- className: `${boxClass} w-full`
2002
+ title: disabled ? disabledTitle : void 0,
2003
+ "aria-label": ariaLabel,
2004
+ "aria-labelledby": ariaLabelledBy,
2005
+ "aria-disabled": disabled || void 0,
2006
+ className: cn(
2007
+ triggerVariant === "settings" ? selectTriggerVariants({
2008
+ size: "default",
2009
+ className: "h-auto min-h-9 max-lg:min-h-11 pointer-coarse:min-h-11 w-full min-w-0 max-w-full whitespace-normal text-left font-medium text-foreground"
2010
+ }) : "inline-flex h-7 min-w-0 max-w-full items-center gap-1 overflow-hidden rounded-md bg-transparent px-1 text-xs font-medium text-foreground/80 transition-colors hover:text-foreground",
2011
+ disabled && "cursor-not-allowed opacity-50 hover:text-foreground/80",
2012
+ className
2013
+ ),
2014
+ children: [
2015
+ triggerVariant === "settings" ? /* @__PURE__ */ jsx4("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: shown ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2016
+ /* @__PURE__ */ jsx4(
2017
+ MakerBrandGlyph,
2018
+ {
2019
+ maker: shown.maker,
2020
+ colored: true,
2021
+ className: "h-3.5 w-3.5 shrink-0"
2022
+ }
2023
+ ),
2024
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0 whitespace-normal text-left leading-tight", children: [
2025
+ shown.name,
2026
+ selectedClassLabel && /* @__PURE__ */ jsxs3("span", { className: "text-muted-foreground", children: [
2027
+ " \xB7 ",
2028
+ selectedClassLabel
2029
+ ] })
2030
+ ] })
2031
+ ] }) : /* @__PURE__ */ jsx4("span", { className: "min-w-0 whitespace-normal text-left leading-tight text-muted-foreground", children: isLoading ? "Loading models\u2026" : emptyOptionLabel && !value ? emptyOptionLabel : placeholder }) }) : /* @__PURE__ */ jsx4("span", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: shown ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2032
+ /* @__PURE__ */ jsx4(
2033
+ MakerBrandGlyph,
2034
+ {
2035
+ maker: shown.maker,
2036
+ colored: true,
2037
+ className: "h-3.5 w-3.5 shrink-0"
2038
+ }
2039
+ ),
2040
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0 truncate", children: [
2041
+ shown.name,
2042
+ selectedClassLabel && /* @__PURE__ */ jsxs3("span", { className: "text-muted-foreground", children: [
2043
+ " \xB7 ",
2044
+ selectedClassLabel
2045
+ ] })
2046
+ ] })
2047
+ ] }) : /* @__PURE__ */ jsx4("span", { className: "min-w-0 truncate text-muted-foreground", children: isLoading ? "Loading models\u2026" : emptyOptionLabel && !value ? emptyOptionLabel : placeholder }) }),
2048
+ /* @__PURE__ */ jsx4(
2049
+ ChevronDownIcon,
2050
+ {
2051
+ className: cn(
2052
+ "shrink-0 text-muted-foreground/60",
2053
+ triggerVariant === "settings" ? "h-4 w-4" : "h-3 w-3"
2054
+ )
2055
+ }
2056
+ )
2057
+ ]
2058
+ }
2059
+ );
2060
+ const listPanel = /* @__PURE__ */ jsxs3("div", { className: "flex h-full flex-col", children: [
2061
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 border-b border-border p-2", children: [
2062
+ /* @__PURE__ */ jsxs3("div", { className: "relative flex-1", children: [
2063
+ /* @__PURE__ */ jsx4(SearchIcon, { className: "absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
2064
+ /* @__PURE__ */ jsx4(
2065
+ Input,
2066
+ {
2067
+ adornment: "start",
2068
+ ref: inputRef,
2069
+ value: query,
2070
+ onChange: (e) => setQuery(e.target.value),
2071
+ placeholder: "Search models\u2026"
2072
+ }
2073
+ )
2074
+ ] }),
2075
+ /* @__PURE__ */ jsxs3(
2076
+ "button",
2077
+ {
2078
+ type: "button",
2079
+ onClick: () => isMobile ? setMobileFilters(true) : setRightPanel(rightPanel === "filters" ? null : "filters"),
2080
+ className: cn(
2081
+ "inline-flex h-7 items-center gap-1 rounded-md border px-2 text-[11px] transition-colors",
2082
+ rightPanel === "filters" ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground"
2083
+ ),
2084
+ children: [
2085
+ /* @__PURE__ */ jsx4(SlidersHorizontalIcon, { className: "h-3.5 w-3.5" }),
2086
+ "Filters",
2087
+ activeFilterCount > 0 && /* @__PURE__ */ jsx4("span", { className: "text-primary", children: activeFilterCount })
2088
+ ]
2089
+ }
2090
+ )
2091
+ ] }),
2092
+ /* @__PURE__ */ jsx4("div", { className: "flex items-center gap-1 border-b border-border px-2 py-1", children: ["all", "favorites"].map((t) => /* @__PURE__ */ jsxs3(
2093
+ "button",
2094
+ {
2095
+ type: "button",
2096
+ onClick: () => setTab(t),
2097
+ className: cn(
2098
+ "inline-flex items-center gap-1 rounded px-2 py-0.5 text-[11px] transition-colors",
2099
+ tab === t ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground"
2100
+ ),
2101
+ children: [
2102
+ t === "favorites" && /* @__PURE__ */ jsx4(
2103
+ StarIcon,
2104
+ {
2105
+ className: cn(
2106
+ "h-3 w-3",
2107
+ tab === t && "fill-amber-400 text-amber-400"
2108
+ )
2109
+ }
2110
+ ),
2111
+ t === "all" ? "All" : "Favorites",
2112
+ t === "favorites" && favoriteSet.size > 0 && /* @__PURE__ */ jsx4("span", { className: "text-muted-foreground", children: favoriteSet.size })
2113
+ ]
2114
+ },
2115
+ t
2116
+ )) }),
2117
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-[16px_20px_minmax(0,1fr)_2.75rem_3rem_2.75rem] gap-2 border-b border-border px-2 py-1 text-[10px] uppercase tracking-wide text-muted-foreground", children: [
2118
+ /* @__PURE__ */ jsx4("span", { className: "w-4" }),
2119
+ /* @__PURE__ */ jsx4("span", { className: "w-5", title: "Maker", "aria-label": "Maker" }),
2120
+ /* @__PURE__ */ jsx4("span", { children: "Name" }),
2121
+ /* @__PURE__ */ jsx4("span", { className: "w-11 text-right", children: "Speed" }),
2122
+ /* @__PURE__ */ jsx4(DelayedHint, { label: "How many tokens this model can hold in one conversation", children: /* @__PURE__ */ jsx4("span", { className: "block w-12 text-right", title: "Context window", children: "Window" }) }),
2123
+ /* @__PURE__ */ jsx4(DelayedHint, { label: "Relative cost band \u2014 more $ is more expensive. Not your personal usage.", children: /* @__PURE__ */ jsx4("span", { className: "block w-[2.75rem] text-right", children: "Cost" }) })
2124
+ ] }),
2125
+ /* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 overflow-y-auto p-1", children: error ? /* @__PURE__ */ jsxs3("div", { className: "p-3 text-xs text-destructive", children: [
2126
+ /* @__PURE__ */ jsxs3("div", { className: "font-medium", children: [
2127
+ "Failed to load ",
2128
+ variant,
2129
+ " catalog"
2130
+ ] }),
2131
+ /* @__PURE__ */ jsx4("div", { className: "mt-0.5 font-mono text-[11px] opacity-80", children: error }),
2132
+ variant === "admin" && /* @__PURE__ */ jsx4("div", { className: "mt-1 text-[11px] text-muted-foreground", children: "If this is a 42501, the admin_model_catalog RPC rejected your role (super admin required)." }),
2133
+ ports.renderErrorAction?.(void 0)
2134
+ ] }) : isLoading ? /* @__PURE__ */ jsx4("div", { className: "p-4 text-center text-xs text-muted-foreground", children: "Loading catalog\u2026" }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
2135
+ emptyOptionLabel && onClear && /* @__PURE__ */ jsx4(
2136
+ "button",
2137
+ {
2138
+ type: "button",
2139
+ onClick: handleClear,
2140
+ className: cn(
2141
+ "mb-1 w-full rounded px-2 py-1.5 text-left text-xs transition-colors hover:bg-muted/60 focus:bg-muted/60 focus:outline-none",
2142
+ !value ? "bg-muted font-medium text-foreground" : "text-muted-foreground"
2143
+ ),
2144
+ children: emptyOptionLabel
2145
+ }
2146
+ ),
2147
+ localRows.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
2148
+ /* @__PURE__ */ jsx4("div", { className: GROUP_HEADING, children: localGroupLabel }),
2149
+ localRows.map((l) => /* @__PURE__ */ jsx4(LocalModelRow, { model: l, selected: l.id === value, onSelect: () => handleSelectLocal(l) }, l.id)),
2150
+ filtered.length > 0 && /* @__PURE__ */ jsx4("div", { className: GROUP_HEADING, children: "Cloud" })
2151
+ ] }),
2152
+ filtered.length === 0 ? localRows.length > 0 ? null : /* @__PURE__ */ jsx4("div", { className: "p-4 text-center text-xs text-muted-foreground", children: "No models match your filters." }) : listRows.map(({ model: m, classTier }) => /* @__PURE__ */ jsx4(
2153
+ ModelRow,
2154
+ {
2155
+ model: m,
2156
+ classTier,
2157
+ tier: costRatingTier(m.costRating),
2158
+ variant,
2159
+ selected: m.id === value && (classTier == null || classTier.offeringId === selectedClass?.offeringId),
2160
+ isFavorite: favoriteSet.has(m.id),
2161
+ locked: !canUseTopTier && isTopTierRating(m.costRating),
2162
+ onToggleFavorite: () => toggleFavorite(m.id),
2163
+ onSelect: () => isMobile ? (setMobileDetail(m), setMobileDetailClass(classTier)) : handleSelectClass(m, classTier),
2164
+ onHover: isMobile ? void 0 : () => {
2165
+ setHovered(m);
2166
+ setHoveredClass(classTier);
2167
+ if (rightPanel !== "filters") setRightPanel("detail");
2168
+ }
2169
+ },
2170
+ classTier ? `${m.id}:${classTier.offeringId}` : m.id
2171
+ ))
2172
+ ] }) }),
2173
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-2 border-t border-border px-2 py-1 text-[10px] text-muted-foreground", children: [
2174
+ /* @__PURE__ */ jsx4("span", { children: error ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2175
+ "Models couldn't be loaded",
2176
+ ports.renderErrorAction?.(String(error))
2177
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
2178
+ listRows.length + localRows.length,
2179
+ " model",
2180
+ listRows.length + localRows.length === 1 ? "" : "s",
2181
+ variant === "user" && (hiddenModelIds?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2182
+ " \xB7 ",
2183
+ ports.settingsHref && LinkComponent ? /* @__PURE__ */ jsxs3(LinkComponent, { href: ports.settingsHref, className: "underline hover:text-foreground", children: [
2184
+ hiddenModelIds?.length,
2185
+ " hidden by you"
2186
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
2187
+ hiddenModelIds?.length,
2188
+ " hidden by you"
2189
+ ] })
2190
+ ] }) : null
2191
+ ] }) }),
2192
+ isSuperAdmin && catalogVariant == null && /* @__PURE__ */ jsxs3(
2193
+ "button",
2194
+ {
2195
+ type: "button",
2196
+ onClick: () => setVariantMode(variant !== "admin"),
2197
+ title: variant === "admin" ? "Admin view \u2014 real vendor/api/$ data. Click to return to the user view." : "Switch to the admin view (real vendor/api/$ data)",
2198
+ className: cn(
2199
+ "inline-flex items-center gap-1 rounded px-1.5 py-0.5 transition-colors",
2200
+ variant === "admin" ? "bg-primary/10 text-foreground" : "hover:text-foreground"
2201
+ ),
2202
+ children: [
2203
+ /* @__PURE__ */ jsx4(ShieldIcon, { className: "h-3 w-3" }),
2204
+ "Admin"
2205
+ ]
2206
+ }
2207
+ )
2208
+ ] })
2209
+ ] });
2210
+ if (disabled) {
2211
+ return trigger;
2212
+ }
2213
+ const mobileBody = /* @__PURE__ */ jsx4("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: mobileDetail ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2214
+ /* @__PURE__ */ jsxs3(
2215
+ "button",
2216
+ {
2217
+ onClick: () => setMobileDetail(null),
2218
+ className: "flex shrink-0 items-center gap-1.5 border-b border-border px-3 py-2.5 text-sm font-medium text-primary",
2219
+ children: [
2220
+ /* @__PURE__ */ jsx4(ChevronRightIcon, { className: "h-4 w-4 rotate-180" }),
2221
+ " Back"
2222
+ ]
2223
+ }
2224
+ ),
2225
+ /* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx4(
2226
+ ModelDetailCard,
2227
+ {
2228
+ model: mobileDetail,
2229
+ tier: costRatingTier(mobileDetail.costRating),
2230
+ variant,
2231
+ onSelect: () => handleSelect(mobileDetail.id, mobileDetailClass),
2232
+ isCurrentModel: mobileDetail.id === value,
2233
+ pinnedOfferingId,
2234
+ onPinOffering: onOfferingPinChange ? handlePinOffering : void 0,
2235
+ includeAdminSection: true
2236
+ }
2237
+ ) })
2238
+ ] }) : mobileFilters ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2239
+ /* @__PURE__ */ jsxs3(
2240
+ "button",
2241
+ {
2242
+ onClick: () => setMobileFilters(false),
2243
+ className: "flex shrink-0 items-center gap-1.5 border-b border-border px-3 py-2.5 text-sm font-medium text-primary",
2244
+ children: [
2245
+ /* @__PURE__ */ jsx4(ChevronRightIcon, { className: "h-4 w-4 rotate-180" }),
2246
+ " Back"
2247
+ ]
2248
+ }
2249
+ ),
2250
+ /* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx4(
2251
+ FiltersPanel,
2252
+ {
2253
+ filters,
2254
+ setFilters,
2255
+ variant,
2256
+ makerOptions,
2257
+ serviceOptions,
2258
+ vendorOptions,
2259
+ apiOptions
2260
+ }
2261
+ ) })
2262
+ ] }) : listPanel });
2263
+ if (isMobile && inline) {
2264
+ return /* @__PURE__ */ jsx4(TooltipProvider, { delayDuration: HOVER_DELAY_MS, skipDelayDuration: HOVER_DELAY_MS, children: mobileBody });
2265
+ }
2266
+ if (isMobile) {
2267
+ return /* @__PURE__ */ jsxs3(Drawer, { open, onOpenChange: handleOpen, children: [
2268
+ /* @__PURE__ */ jsx4(DrawerTrigger, { asChild: true, children: trigger }),
2269
+ /* @__PURE__ */ jsx4(DrawerContent, { className: "h-[85dvh]", children: /* @__PURE__ */ jsxs3(
2270
+ TooltipProvider,
2271
+ {
2272
+ delayDuration: HOVER_DELAY_MS,
2273
+ skipDelayDuration: HOVER_DELAY_MS,
2274
+ children: [
2275
+ /* @__PURE__ */ jsx4(DrawerTitle, { className: "sr-only", children: "Select model" }),
2276
+ mobileBody
2277
+ ]
2278
+ }
2279
+ ) })
2280
+ ] });
2281
+ }
2282
+ const popoverWidth = LIST_WIDTH + DETAIL_WIDTH + (variant === "admin" ? ADMIN_PANEL_WIDTH : 0);
2283
+ const adminPanelModel = hovered ?? selected;
2284
+ const desktopBody = /* @__PURE__ */ jsx4(
2285
+ TooltipProvider,
2286
+ {
2287
+ delayDuration: HOVER_DELAY_MS,
2288
+ skipDelayDuration: HOVER_DELAY_MS,
2289
+ children: /* @__PURE__ */ jsx4("div", { className: "h-full overflow-x-auto overflow-y-hidden", children: /* @__PURE__ */ jsxs3(
2290
+ "div",
2291
+ {
2292
+ className: "flex h-full",
2293
+ style: inline ? void 0 : { minWidth: popoverWidth },
2294
+ children: [
2295
+ /* @__PURE__ */ jsx4(
2296
+ "div",
2297
+ {
2298
+ className: "flex h-full min-h-0 flex-col border-r border-border",
2299
+ style: inline ? { flex: `0 1 ${LIST_WIDTH}px`, minWidth: INLINE_LIST_MIN_WIDTH } : { width: LIST_WIDTH, flexShrink: 0 },
2300
+ children: listPanel
2301
+ }
2302
+ ),
2303
+ /* @__PURE__ */ jsx4(
2304
+ "div",
2305
+ {
2306
+ className: "flex h-full min-h-0 flex-col overflow-hidden",
2307
+ style: inline ? { flex: "1 1 0", minWidth: INLINE_DETAIL_MIN_WIDTH } : { width: DETAIL_WIDTH, flexShrink: 0 },
2308
+ children: rightPanel === "filters" ? /* @__PURE__ */ jsx4(
2309
+ FiltersPanel,
2310
+ {
2311
+ filters,
2312
+ setFilters,
2313
+ variant,
2314
+ makerOptions,
2315
+ serviceOptions,
2316
+ vendorOptions,
2317
+ apiOptions
2318
+ }
2319
+ ) : rightPanel === "detail" && hovered ? /* @__PURE__ */ jsx4(
2320
+ "div",
2321
+ {
2322
+ className: "h-full animate-in fade-in-0 duration-300",
2323
+ children: /* @__PURE__ */ jsx4(
2324
+ ModelDetailCard,
2325
+ {
2326
+ model: hovered,
2327
+ tier: costRatingTier(hovered.costRating),
2328
+ variant,
2329
+ onSelect: () => handleSelect(hovered.id, hoveredClass),
2330
+ isCurrentModel: hovered.id === value,
2331
+ pinnedOfferingId,
2332
+ onPinOffering: onOfferingPinChange ? handlePinOffering : void 0
2333
+ }
2334
+ )
2335
+ },
2336
+ hovered.id
2337
+ ) : /* @__PURE__ */ jsxs3("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 px-6 text-center", children: [
2338
+ /* @__PURE__ */ jsx4(MousePointerClickIcon, { className: "h-6 w-6 text-muted-foreground/40" }),
2339
+ /* @__PURE__ */ jsx4("p", { className: "text-xs leading-relaxed text-muted-foreground/70", children: "Hover a model to preview its details, or click to select." })
2340
+ ] })
2341
+ }
2342
+ ),
2343
+ variant === "admin" && /* @__PURE__ */ jsx4(
2344
+ "div",
2345
+ {
2346
+ className: "flex h-full min-h-0 shrink-0 flex-col overflow-hidden border-l border-border",
2347
+ style: { width: ADMIN_PANEL_WIDTH },
2348
+ children: adminPanelModel ? /* @__PURE__ */ jsx4(AdminOfferingsSection, { model: adminPanelModel }) : /* @__PURE__ */ jsxs3("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 px-6 text-center", children: [
2349
+ /* @__PURE__ */ jsx4(ShieldIcon, { className: "h-6 w-6 text-muted-foreground/40" }),
2350
+ /* @__PURE__ */ jsx4("p", { className: "text-xs leading-relaxed text-muted-foreground/70", children: "Hover a model to see every offering's real vendor, api, provider model id and $ pricing." })
2351
+ ] })
2352
+ }
2353
+ )
2354
+ ]
2355
+ }
2356
+ ) })
2357
+ }
2358
+ );
2359
+ if (inline) {
2360
+ return /* @__PURE__ */ jsx4("div", { className: "h-full w-full min-w-0", children: desktopBody });
2361
+ }
2362
+ return /* @__PURE__ */ jsxs3(Popover, { open, onOpenChange: handleOpen, modal: false, children: [
2363
+ /* @__PURE__ */ jsx4(PopoverTrigger, { asChild: true, children: trigger }),
2364
+ /* @__PURE__ */ jsx4(
2365
+ PopoverContent,
2366
+ {
2367
+ align: "start",
2368
+ sideOffset: 4,
2369
+ collisionPadding: 12,
2370
+ sticky: "always",
2371
+ ...dialogContainer ? { container: dialogContainer } : {},
2372
+ className: "overflow-hidden p-0",
2373
+ style: {
2374
+ // PopoverContent's 1px border is inside this border-box width.
2375
+ // Give the fixed columns those two pixels at their natural desktop width;
2376
+ // maxWidth still turns the inner panel into a horizontal scrollport.
2377
+ width: popoverWidth + 2,
2378
+ maxWidth: "calc(100vw - 24px)",
2379
+ height: PANEL_HEIGHT,
2380
+ // See LIST_MAX_HEIGHT — this clamp is what keeps the search row on
2381
+ // screen at an 800px-tall window.
2382
+ maxHeight: LIST_MAX_HEIGHT
2383
+ },
2384
+ children: desktopBody
2385
+ }
2386
+ )
2387
+ ] });
2388
+ }
2389
+
2390
+ // settings/settings-catalogue.ts
2391
+ import { humanizeIdentifier } from "@ai-matrx/kit/text-case";
2392
+ var CONTROL_TYPES = [
2393
+ "number",
2394
+ "integer",
2395
+ "boolean",
2396
+ "string",
2397
+ "enum",
2398
+ "array",
2399
+ "string_array",
2400
+ "object_array"
2401
+ ];
2402
+ function isControlDefinitionShape(value) {
2403
+ return typeof value.type === "string" && CONTROL_TYPES.includes(value.type);
2404
+ }
2405
+ var TEXT_GROUP = [
2406
+ { key: "response_format", label: "Response Format" },
2407
+ { key: "stop_sequences", label: "Stop Sequences" },
2408
+ { key: "temperature", label: "Temperature" },
2409
+ { key: "max_output_tokens", label: "Max Output Tokens" },
2410
+ { key: "top_p", label: "Top P" },
2411
+ { key: "top_k", label: "Top K" },
2412
+ { key: "thinking_budget", label: "Thinking Budget" },
2413
+ { key: "thinking_level", label: "Thinking Level" },
2414
+ { key: "reasoning_effort", label: "Reasoning Effort" },
2415
+ { key: "reasoning_summary", label: "Reasoning Summary" },
2416
+ // Deep Research agents: charts and images inside the report.
2417
+ { key: "visualization", label: "Charts and Images" },
2418
+ { key: "verbosity", label: "Verbosity" },
2419
+ { key: "tool_choice", label: "Tool Choice" }
2420
+ ];
2421
+ var FEATURE_FLAGS_GROUP = [
2422
+ { key: "stream", label: "Stream Response" },
2423
+ { key: "store", label: "Store Conversation" },
2424
+ { key: "parallel_tool_calls", label: "Parallel Tool Calls" },
2425
+ { key: "include_thoughts", label: "Include Thoughts" },
2426
+ { key: "internal_web_search", label: "Internal Web Search" },
2427
+ { key: "internal_url_context", label: "Internal URL Context" },
2428
+ { key: "disable_safety_checker", label: "Disable Safety Checker" },
2429
+ { key: "clear_thinking", label: "Clear Thinking" },
2430
+ { key: "disable_reasoning", label: "Disable Reasoning" },
2431
+ { key: "generate_audio", label: "Generate Audio" },
2432
+ { key: "enhance_prompt", label: "Enhance Prompt" },
2433
+ { key: "include_rai_reason", label: "Include RAI Reason" }
2434
+ ];
2435
+ var IMAGE_VIDEO_GROUP = [
2436
+ { key: "size", label: "Size" },
2437
+ { key: "quality", label: "Quality" },
2438
+ { key: "count", label: "Count" },
2439
+ { key: "steps", label: "Steps" },
2440
+ { key: "guidance_scale", label: "Guidance Scale" },
2441
+ { key: "seed", label: "Seed" },
2442
+ { key: "width", label: "Width" },
2443
+ { key: "height", label: "Height" },
2444
+ { key: "fps", label: "FPS" },
2445
+ { key: "seconds", label: "Duration (s) [legacy string]" },
2446
+ { key: "output_quality", label: "Output Quality" },
2447
+ { key: "negative_prompt", label: "Negative Prompt" },
2448
+ { key: "aspect_ratio", label: "Aspect Ratio" },
2449
+ { key: "ratio", label: "Aspect Ratio (Together/Runway)" },
2450
+ { key: "resolution", label: "Resolution" },
2451
+ { key: "image_size", label: "Image Size" },
2452
+ { key: "num_outputs", label: "Outputs" },
2453
+ { key: "number_of_images", label: "Number of Images" },
2454
+ { key: "render_quality", label: "Render Quality" },
2455
+ { key: "background", label: "Background" },
2456
+ { key: "input_fidelity", label: "Input Fidelity" },
2457
+ { key: "output_compression", label: "Output Compression" },
2458
+ { key: "moderation", label: "Moderation" },
2459
+ { key: "partial_images", label: "Partial Images" },
2460
+ { key: "output_mime_type", label: "Output MIME Type" },
2461
+ { key: "person_generation", label: "Person Generation" },
2462
+ { key: "image_format", label: "Image Format" },
2463
+ { key: "style", label: "Style" },
2464
+ { key: "reference_strength", label: "Reference Strength" },
2465
+ { key: "duration_seconds", label: "Duration (s)" },
2466
+ { key: "duration", label: "Duration" },
2467
+ { key: "encode_quality", label: "Encode Quality" },
2468
+ { key: "video_action", label: "Video Action" },
2469
+ { key: "reference_images", label: "Reference Images" },
2470
+ { key: "frame_images", label: "Frame Images" },
2471
+ { key: "image_loras", label: "Image LoRAs" }
2472
+ ];
2473
+ var AUDIO_GROUP = [
2474
+ { key: "tts_voice", label: "Voice" },
2475
+ { key: "performance_direction", label: "Performance Direction" },
2476
+ { key: "speech_speed", label: "Speed" },
2477
+ { key: "turn_pause_ms", label: "Pause Between Turns (ms)" },
2478
+ { key: "language_code", label: "Language" },
2479
+ { key: "audio_format", label: "Audio Format" }
2480
+ ];
2481
+ var SETTINGS_CATALOGUE = [
2482
+ { id: "text", label: "", entries: TEXT_GROUP },
2483
+ { id: "featureFlags", label: "Feature Flags", entries: FEATURE_FLAGS_GROUP },
2484
+ { id: "imageVideo", label: "Image / Video Settings", entries: IMAGE_VIDEO_GROUP },
2485
+ { id: "audio", label: "Audio Settings", entries: AUDIO_GROUP }
2486
+ ];
2487
+ var CATALOGUE_KEYS = new Set(
2488
+ SETTINGS_CATALOGUE.flatMap((g) => g.entries.map((e) => e.key))
2489
+ );
2490
+ var CATCHALL_EXCLUDED = /* @__PURE__ */ new Set([
2491
+ "rawControls",
2492
+ "unmappedControls",
2493
+ "model_id",
2494
+ "model",
2495
+ // chosen via the dedicated model picker, never a generic settings row
2496
+ "multi_speaker",
2497
+ // edited via the Voice editor alongside tts_voice
2498
+ // Model-gated UI flags live in the ui_gates column + UiGatesEditor, never as
2499
+ // settings rows — even when the model declares them as controls (e.g. every
2500
+ // model now declares `tools` for the tool-support capability).
2501
+ ...UI_GATE_KEYS
2502
+ ]);
2503
+ function lookupControl(controls, key) {
2504
+ if (!controls) return null;
2505
+ if (CATCHALL_EXCLUDED.has(key)) {
2506
+ if (key === "rawControls" || key === "unmappedControls") return null;
2507
+ }
2508
+ const candidate = controls[key];
2509
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
2510
+ const c = candidate;
2511
+ if (isControlDefinitionShape(c)) {
2512
+ return c;
2513
+ }
2514
+ }
2515
+ return null;
2516
+ }
2517
+ function hasMeaningfulValue(settings, key) {
2518
+ if (!settings) return false;
2519
+ const v = settings[key];
2520
+ return v !== void 0 && v !== null;
2521
+ }
2522
+ function buildSettingsRows(controls, settings) {
2523
+ const groups = SETTINGS_CATALOGUE.map((group) => ({
2524
+ id: group.id,
2525
+ label: group.label,
2526
+ rows: group.entries.flatMap((entry) => {
2527
+ const control = lookupControl(controls, entry.key);
2528
+ if (!control) return [];
2529
+ return [
2530
+ {
2531
+ key: entry.key,
2532
+ label: entry.label,
2533
+ group: group.id,
2534
+ control,
2535
+ supported: true,
2536
+ hasValue: hasMeaningfulValue(settings, entry.key)
2537
+ }
2538
+ ];
2539
+ })
2540
+ }));
2541
+ const declaredExtra = [];
2542
+ if (controls) {
2543
+ for (const key of Object.keys(controls)) {
2544
+ if (CATALOGUE_KEYS.has(key) || CATCHALL_EXCLUDED.has(key)) continue;
2545
+ if (lookupControl(controls, key)) declaredExtra.push(key);
521
2546
  }
522
- );
2547
+ }
2548
+ if (declaredExtra.length > 0) {
2549
+ const rows = declaredExtra.sort().map((key) => ({
2550
+ key,
2551
+ label: humanizeIdentifier(key),
2552
+ group: "other",
2553
+ control: lookupControl(controls, key),
2554
+ supported: true,
2555
+ hasValue: hasMeaningfulValue(settings, key)
2556
+ }));
2557
+ groups.push({ id: "other", label: "Other Settings", rows });
2558
+ }
2559
+ return groups;
2560
+ }
2561
+ var CATALOGUE_LABELS = new Map(
2562
+ SETTINGS_CATALOGUE.flatMap((g) => g.entries.map((e) => [e.key, e.label]))
2563
+ );
2564
+ function settingLabel(key) {
2565
+ return CATALOGUE_LABELS.get(key) ?? humanizeIdentifier(key);
2566
+ }
2567
+ var RECOGNIZED_PARAMS = new Set(LLM_PARAMS_KEYS);
2568
+ var EXTRA_EXCLUDED = /* @__PURE__ */ new Set([
2569
+ ...CATCHALL_EXCLUDED,
2570
+ "offering_id",
2571
+ "max_tokens",
2572
+ "output_format",
2573
+ "n"
2574
+ ]);
2575
+ function extraSettingKeys(controls, settings, alsoKeys = []) {
2576
+ const out = { translated: [], unsupported: [] };
2577
+ if (!controls) return out;
2578
+ const declaresAny = Object.keys(controls).some((key) => lookupControl(controls, key) !== null);
2579
+ if (!declaresAny) return out;
2580
+ const keys = new Set(Object.keys(settings ?? {}).filter((key) => hasMeaningfulValue(settings, key)));
2581
+ for (const key of alsoKeys) keys.add(key);
2582
+ for (const key of keys) {
2583
+ if (EXTRA_EXCLUDED.has(key) || lookupControl(controls, key)) continue;
2584
+ (RECOGNIZED_PARAMS.has(key) ? out.translated : out.unsupported).push(key);
2585
+ }
2586
+ return out;
2587
+ }
2588
+
2589
+ // settings/setting-state.ts
2590
+ var ENUM_OFF_VALUE = "none";
2591
+ function isOffValue(value, control) {
2592
+ if (value === false) return true;
2593
+ if (value === ENUM_OFF_VALUE) {
2594
+ return !control || control.type === "enum";
2595
+ }
2596
+ return false;
523
2597
  }
524
2598
 
525
2599
  // settings/react/InputCapabilitiesEditor.tsx
526
- import { ErrorBox, Switch } from "@ai-matrx/design-system";
527
- import { Button } from "@ai-matrx/design-system/controls";
2600
+ import { ErrorBox } from "@ai-matrx/design-system";
2601
+ import { Button, Switch } from "@ai-matrx/design-system/controls";
528
2602
 
529
2603
  // settings/react/settings-icons.tsx
530
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
531
- function Svg({ className, children }) {
532
- return /* @__PURE__ */ jsx2(
2604
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2605
+ function Svg3({ className, children }) {
2606
+ return /* @__PURE__ */ jsx5(
533
2607
  "svg",
534
2608
  {
535
2609
  xmlns: "http://www.w3.org/2000/svg",
@@ -545,98 +2619,98 @@ function Svg({ className, children }) {
545
2619
  }
546
2620
  );
547
2621
  }
548
- var CircleCheckGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
549
- /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
550
- /* @__PURE__ */ jsx2("path", { d: "m16 9-5.5 5.5L8 12" })
2622
+ var CircleCheckGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2623
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10" }),
2624
+ /* @__PURE__ */ jsx5("path", { d: "m16 9-5.5 5.5L8 12" })
551
2625
  ] });
552
- var TriangleAlertGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
553
- /* @__PURE__ */ jsx2("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
554
- /* @__PURE__ */ jsx2("path", { d: "M12 9v4" }),
555
- /* @__PURE__ */ jsx2("path", { d: "M12 17h.01" })
2626
+ var TriangleAlertGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2627
+ /* @__PURE__ */ jsx5("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
2628
+ /* @__PURE__ */ jsx5("path", { d: "M12 9v4" }),
2629
+ /* @__PURE__ */ jsx5("path", { d: "M12 17h.01" })
556
2630
  ] });
557
- var VariableGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
558
- /* @__PURE__ */ jsx2("path", { d: "M8 21s-4-3-4-9 4-9 4-9" }),
559
- /* @__PURE__ */ jsx2("path", { d: "M16 3s4 3 4 9-4 9-4 9" }),
560
- /* @__PURE__ */ jsx2("line", { x1: "15", x2: "9", y1: "9", y2: "15" }),
561
- /* @__PURE__ */ jsx2("line", { x1: "9", x2: "15", y1: "9", y2: "15" })
2631
+ var VariableGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2632
+ /* @__PURE__ */ jsx5("path", { d: "M8 21s-4-3-4-9 4-9 4-9" }),
2633
+ /* @__PURE__ */ jsx5("path", { d: "M16 3s4 3 4 9-4 9-4 9" }),
2634
+ /* @__PURE__ */ jsx5("line", { x1: "15", x2: "9", y1: "9", y2: "15" }),
2635
+ /* @__PURE__ */ jsx5("line", { x1: "9", x2: "15", y1: "9", y2: "15" })
562
2636
  ] });
563
- var UnlinkGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
564
- /* @__PURE__ */ jsx2("path", { d: "m18.84 12.25 1.72-1.71h-.02a5.004 5.004 0 0 0-.12-7.07 5.006 5.006 0 0 0-6.95 0l-1.72 1.71" }),
565
- /* @__PURE__ */ jsx2("path", { d: "m5.17 11.75-1.71 1.71a5.004 5.004 0 0 0 .12 7.07 5.006 5.006 0 0 0 6.95 0l1.71-1.71" }),
566
- /* @__PURE__ */ jsx2("line", { x1: "8", x2: "8", y1: "2", y2: "5" }),
567
- /* @__PURE__ */ jsx2("line", { x1: "2", x2: "5", y1: "8", y2: "8" }),
568
- /* @__PURE__ */ jsx2("line", { x1: "16", x2: "16", y1: "19", y2: "22" }),
569
- /* @__PURE__ */ jsx2("line", { x1: "19", x2: "22", y1: "16", y2: "16" })
2637
+ var UnlinkGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2638
+ /* @__PURE__ */ jsx5("path", { d: "m18.84 12.25 1.72-1.71h-.02a5.004 5.004 0 0 0-.12-7.07 5.006 5.006 0 0 0-6.95 0l-1.72 1.71" }),
2639
+ /* @__PURE__ */ jsx5("path", { d: "m5.17 11.75-1.71 1.71a5.004 5.004 0 0 0 .12 7.07 5.006 5.006 0 0 0 6.95 0l1.71-1.71" }),
2640
+ /* @__PURE__ */ jsx5("line", { x1: "8", x2: "8", y1: "2", y2: "5" }),
2641
+ /* @__PURE__ */ jsx5("line", { x1: "2", x2: "5", y1: "8", y2: "8" }),
2642
+ /* @__PURE__ */ jsx5("line", { x1: "16", x2: "16", y1: "19", y2: "22" }),
2643
+ /* @__PURE__ */ jsx5("line", { x1: "19", x2: "22", y1: "16", y2: "16" })
570
2644
  ] });
571
- var WrenchGlyph = ({ className }) => /* @__PURE__ */ jsx2(Svg, { className, children: /* @__PURE__ */ jsx2("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" }) });
572
- var Trash2Glyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
573
- /* @__PURE__ */ jsx2("path", { d: "M10 11v6" }),
574
- /* @__PURE__ */ jsx2("path", { d: "M14 11v6" }),
575
- /* @__PURE__ */ jsx2("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
576
- /* @__PURE__ */ jsx2("path", { d: "M3 6h18" }),
577
- /* @__PURE__ */ jsx2("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
2645
+ var WrenchGlyph = ({ className }) => /* @__PURE__ */ jsx5(Svg3, { className, children: /* @__PURE__ */ jsx5("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" }) });
2646
+ var Trash2Glyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2647
+ /* @__PURE__ */ jsx5("path", { d: "M10 11v6" }),
2648
+ /* @__PURE__ */ jsx5("path", { d: "M14 11v6" }),
2649
+ /* @__PURE__ */ jsx5("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
2650
+ /* @__PURE__ */ jsx5("path", { d: "M3 6h18" }),
2651
+ /* @__PURE__ */ jsx5("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
578
2652
  ] });
579
- var RotateCcwGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
580
- /* @__PURE__ */ jsx2("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
581
- /* @__PURE__ */ jsx2("path", { d: "M3 3v5h5" })
2653
+ var RotateCcwGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2654
+ /* @__PURE__ */ jsx5("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
2655
+ /* @__PURE__ */ jsx5("path", { d: "M3 3v5h5" })
582
2656
  ] });
583
- var ImageGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
584
- /* @__PURE__ */ jsx2("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
585
- /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "2" }),
586
- /* @__PURE__ */ jsx2("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
2657
+ var ImageGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2658
+ /* @__PURE__ */ jsx5("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
2659
+ /* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "2" }),
2660
+ /* @__PURE__ */ jsx5("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
587
2661
  ] });
588
- var FileTextGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
589
- /* @__PURE__ */ jsx2("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
590
- /* @__PURE__ */ jsx2("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
591
- /* @__PURE__ */ jsx2("path", { d: "M10 9H8" }),
592
- /* @__PURE__ */ jsx2("path", { d: "M16 13H8" }),
593
- /* @__PURE__ */ jsx2("path", { d: "M16 17H8" })
2662
+ var FileTextGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2663
+ /* @__PURE__ */ jsx5("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
2664
+ /* @__PURE__ */ jsx5("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
2665
+ /* @__PURE__ */ jsx5("path", { d: "M10 9H8" }),
2666
+ /* @__PURE__ */ jsx5("path", { d: "M16 13H8" }),
2667
+ /* @__PURE__ */ jsx5("path", { d: "M16 17H8" })
594
2668
  ] });
595
- var YoutubeGlyph = ({ className }) => /* @__PURE__ */ jsxs2(Svg, { className, children: [
596
- /* @__PURE__ */ jsx2("path", { d: "M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17" }),
597
- /* @__PURE__ */ jsx2("path", { d: "m10 15 5-3-5-3z" })
2669
+ var YoutubeGlyph = ({ className }) => /* @__PURE__ */ jsxs4(Svg3, { className, children: [
2670
+ /* @__PURE__ */ jsx5("path", { d: "M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17" }),
2671
+ /* @__PURE__ */ jsx5("path", { d: "m10 15 5-3-5-3z" })
598
2672
  ] });
599
2673
 
600
2674
  // settings/react/SettingRowFrame.tsx
601
- import { Label as Label2 } from "@ai-matrx/design-system";
602
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
603
- var cn = (...parts) => parts.filter(Boolean).join(" ");
2675
+ import { Label } from "@ai-matrx/design-system";
2676
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2677
+ var cn2 = (...parts) => parts.filter(Boolean).join(" ");
604
2678
  function formatSettingValue(value) {
605
2679
  if (typeof value === "boolean") return value ? "On" : "Off";
606
2680
  if (value !== null && typeof value === "object") return JSON.stringify(value);
607
2681
  return String(value);
608
2682
  }
609
2683
  function SettingRowFrame({ rowKey, state, leading, label, children, actions }) {
610
- return /* @__PURE__ */ jsxs3(
2684
+ return /* @__PURE__ */ jsxs5(
611
2685
  "div",
612
2686
  {
613
2687
  "data-setting-row": rowKey,
614
2688
  "data-setting-state": state,
615
2689
  className: "grid grid-cols-[1rem_minmax(0,1fr)_auto] items-start gap-x-2 gap-y-1 py-1 @md:grid-cols-[1rem_9rem_minmax(0,1fr)_auto]",
616
2690
  children: [
617
- /* @__PURE__ */ jsx3("div", { className: "flex h-7 items-center", children: leading }),
618
- /* @__PURE__ */ jsx3("div", { className: "flex min-h-7 min-w-0 items-center", children: label }),
619
- children !== void 0 ? /* @__PURE__ */ jsx3("div", { className: "col-span-2 col-start-2 row-start-2 min-w-0 @md:col-span-1 @md:col-start-3 @md:row-start-1", children }) : null,
620
- /* @__PURE__ */ jsx3("div", { className: "col-start-3 row-start-1 flex min-h-7 items-center justify-end @md:col-start-4", children: actions })
2691
+ /* @__PURE__ */ jsx6("div", { className: "flex h-7 items-center", children: leading }),
2692
+ /* @__PURE__ */ jsx6("div", { className: "flex min-h-7 min-w-0 items-center", children: label }),
2693
+ children !== void 0 ? /* @__PURE__ */ jsx6("div", { className: "col-span-2 col-start-2 row-start-2 min-w-0 @md:col-span-1 @md:col-start-3 @md:row-start-1", children }) : null,
2694
+ /* @__PURE__ */ jsx6("div", { className: "col-start-3 row-start-1 flex min-h-7 items-center justify-end @md:col-start-4", children: actions })
621
2695
  ]
622
2696
  }
623
2697
  );
624
2698
  }
625
2699
  function RowLabel({ htmlFor, muted, children }) {
626
- return /* @__PURE__ */ jsx3(Label2, { htmlFor, className: cn("min-w-0 truncate type-body", muted ? "text-muted-foreground" : "text-foreground"), children });
2700
+ return /* @__PURE__ */ jsx6(Label, { htmlFor, className: cn2("min-w-0 truncate type-body", muted ? "text-muted-foreground" : "text-foreground"), children });
627
2701
  }
628
2702
  function RowText({ children, mono }) {
629
- return /* @__PURE__ */ jsx3("span", { className: cn("flex min-h-7 items-center truncate type-secondary text-muted-foreground", mono && "font-mono"), children });
2703
+ return /* @__PURE__ */ jsx6("span", { className: cn2("flex min-h-7 items-center truncate type-secondary text-muted-foreground", mono && "font-mono"), children });
630
2704
  }
631
2705
  function SettingsGroupSection({ id, title, children }) {
632
- return /* @__PURE__ */ jsxs3("section", { "data-settings-group": id, className: title ? "mt-2 border-t border-border pt-2" : void 0, children: [
633
- title ? /* @__PURE__ */ jsx3("h3", { className: "pb-1 type-title text-foreground", children: title }) : null,
2706
+ return /* @__PURE__ */ jsxs5("section", { "data-settings-group": id, className: title ? "mt-2 border-t border-border pt-2" : void 0, children: [
2707
+ title ? /* @__PURE__ */ jsx6("h3", { className: "pb-1 type-title text-foreground", children: title }) : null,
634
2708
  children
635
2709
  ] });
636
2710
  }
637
2711
 
638
2712
  // settings/react/InputCapabilitiesEditor.tsx
639
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2713
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
640
2714
  var GATE_META = {
641
2715
  image_urls: {
642
2716
  label: "Image URLs",
@@ -665,24 +2739,24 @@ function InputCapabilitiesEditor({
665
2739
  title = "Input Capabilities",
666
2740
  disabled = false
667
2741
  }) {
668
- return /* @__PURE__ */ jsxs4(SettingsGroupSection, { id: "input-capabilities", title, children: [
2742
+ return /* @__PURE__ */ jsxs6(SettingsGroupSection, { id: "input-capabilities", title, children: [
669
2743
  UI_GATE_EDITABLE_KEYS.map((key) => {
670
2744
  const { label, description, Icon } = GATE_META[key];
671
2745
  const switchId = `${idPrefix}-${key}`;
672
2746
  const changed = changedKeys?.has(key) === true;
673
- return /* @__PURE__ */ jsx4(
2747
+ return /* @__PURE__ */ jsx7(
674
2748
  SettingRowFrame,
675
2749
  {
676
2750
  rowKey: key,
677
2751
  state: values[key] === true ? "on" : "off",
678
- leading: /* @__PURE__ */ jsx4(Icon, { className: "h-4 w-4 text-muted-foreground" }),
679
- label: /* @__PURE__ */ jsx4(RowLabel, { htmlFor: switchId, children: label }),
680
- actions: /* @__PURE__ */ jsxs4(Fragment, { children: [
681
- changed && onReset ? /* @__PURE__ */ jsx4(
2752
+ leading: /* @__PURE__ */ jsx7(Icon, { className: "h-4 w-4 text-muted-foreground" }),
2753
+ label: /* @__PURE__ */ jsx7(RowLabel, { htmlFor: switchId, children: label }),
2754
+ actions: /* @__PURE__ */ jsxs6(Fragment3, { children: [
2755
+ changed && onReset ? /* @__PURE__ */ jsx7(
682
2756
  Button,
683
2757
  {
684
2758
  variant: "quiet",
685
- icon: /* @__PURE__ */ jsx4(RotateCcwGlyph, {}),
2759
+ icon: /* @__PURE__ */ jsx7(RotateCcwGlyph, {}),
686
2760
  onClick: () => onReset(key),
687
2761
  disabled,
688
2762
  "aria-label": `${resetLabel}: ${label}`,
@@ -690,225 +2764,379 @@ function InputCapabilitiesEditor({
690
2764
  "data-setting-reset": key
691
2765
  }
692
2766
  ) : null,
693
- /* @__PURE__ */ jsx4(
2767
+ /* @__PURE__ */ jsx7(
694
2768
  Switch,
695
2769
  {
696
2770
  id: switchId,
697
2771
  checked: values[key] === true,
698
2772
  onCheckedChange: (next) => onChange(key, next),
699
- disabled,
700
- className: "shrink-0"
2773
+ disabled
701
2774
  }
702
2775
  )
703
2776
  ] }),
704
- children: /* @__PURE__ */ jsx4(RowText, { children: description })
2777
+ children: /* @__PURE__ */ jsx7(RowText, { children: description })
705
2778
  },
706
2779
  key
707
2780
  );
708
2781
  }),
709
- error ? /* @__PURE__ */ jsx4(ErrorBox, { as: "p", className: "py-1 type-meta text-destructive", message: error }) : null
2782
+ error ? /* @__PURE__ */ jsx7(ErrorBox, { as: "p", className: "py-1 type-meta text-destructive", message: error }) : null
710
2783
  ] });
711
2784
  }
712
2785
 
713
- // settings/react/ModelSettingsPanel.tsx
714
- import { Checkbox as Checkbox2 } from "@ai-matrx/design-system";
715
- import { Button as Button2, Chip } from "@ai-matrx/design-system/controls";
716
-
717
- // settings/settings-catalogue.ts
718
- import { humanizeIdentifier } from "@ai-matrx/kit/text-case";
719
- var CONTROL_TYPES = [
720
- "number",
721
- "integer",
722
- "boolean",
723
- "string",
724
- "enum",
725
- "array",
726
- "string_array",
727
- "object_array"
728
- ];
729
- function isControlDefinitionShape(value) {
730
- return typeof value.type === "string" && CONTROL_TYPES.includes(value.type);
2786
+ // settings/react/SettingControlInput.tsx
2787
+ import { useEffect as useEffect3, useState as useState2 } from "react";
2788
+ import {
2789
+ Checkbox,
2790
+ Input as Input2,
2791
+ Label as Label2,
2792
+ Select,
2793
+ SelectContent,
2794
+ SelectItem,
2795
+ SelectTrigger,
2796
+ SelectValue,
2797
+ Slider,
2798
+ Textarea,
2799
+ Tooltip as Tooltip2,
2800
+ TooltipContent as TooltipContent2,
2801
+ TooltipProvider as TooltipProvider2,
2802
+ TooltipTrigger as TooltipTrigger2
2803
+ } from "@ai-matrx/design-system";
2804
+ import { AspectRatioSelect, OptionCombobox, isAspectRatioOptionSet } from "@ai-matrx/design-system/controls";
2805
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2806
+ var SELECT_MAX_OPTIONS = 12;
2807
+ function choiceFor(options) {
2808
+ if (isAspectRatioOptionSet(options)) return "aspect-ratio";
2809
+ if (options.length > SELECT_MAX_OPTIONS) return "searchable";
2810
+ return "select";
731
2811
  }
732
- var TEXT_GROUP = [
733
- { key: "response_format", label: "Response Format" },
734
- { key: "stop_sequences", label: "Stop Sequences" },
735
- { key: "temperature", label: "Temperature" },
736
- { key: "max_output_tokens", label: "Max Output Tokens" },
737
- { key: "top_p", label: "Top P" },
738
- { key: "top_k", label: "Top K" },
739
- { key: "thinking_budget", label: "Thinking Budget" },
740
- { key: "thinking_level", label: "Thinking Level" },
741
- { key: "reasoning_effort", label: "Reasoning Effort" },
742
- { key: "reasoning_summary", label: "Reasoning Summary" },
743
- // Deep Research agents: charts and images inside the report.
744
- { key: "visualization", label: "Charts and Images" },
745
- { key: "verbosity", label: "Verbosity" },
746
- { key: "tool_choice", label: "Tool Choice" }
747
- ];
748
- var FEATURE_FLAGS_GROUP = [
749
- { key: "stream", label: "Stream Response" },
750
- { key: "store", label: "Store Conversation" },
751
- { key: "parallel_tool_calls", label: "Parallel Tool Calls" },
752
- { key: "include_thoughts", label: "Include Thoughts" },
753
- { key: "internal_web_search", label: "Internal Web Search" },
754
- { key: "internal_url_context", label: "Internal URL Context" },
755
- { key: "disable_safety_checker", label: "Disable Safety Checker" },
756
- { key: "clear_thinking", label: "Clear Thinking" },
757
- { key: "disable_reasoning", label: "Disable Reasoning" },
758
- { key: "generate_audio", label: "Generate Audio" },
759
- { key: "enhance_prompt", label: "Enhance Prompt" },
760
- { key: "include_rai_reason", label: "Include RAI Reason" }
761
- ];
762
- var IMAGE_VIDEO_GROUP = [
763
- { key: "size", label: "Size" },
764
- { key: "quality", label: "Quality" },
765
- { key: "count", label: "Count" },
766
- { key: "steps", label: "Steps" },
767
- { key: "guidance_scale", label: "Guidance Scale" },
768
- { key: "seed", label: "Seed" },
769
- { key: "width", label: "Width" },
770
- { key: "height", label: "Height" },
771
- { key: "fps", label: "FPS" },
772
- { key: "seconds", label: "Duration (s) [legacy string]" },
773
- { key: "output_quality", label: "Output Quality" },
774
- { key: "negative_prompt", label: "Negative Prompt" },
775
- { key: "aspect_ratio", label: "Aspect Ratio" },
776
- { key: "ratio", label: "Aspect Ratio (Together/Runway)" },
777
- { key: "resolution", label: "Resolution" },
778
- { key: "image_size", label: "Image Size" },
779
- { key: "num_outputs", label: "Outputs" },
780
- { key: "number_of_images", label: "Number of Images" },
781
- { key: "render_quality", label: "Render Quality" },
782
- { key: "background", label: "Background" },
783
- { key: "input_fidelity", label: "Input Fidelity" },
784
- { key: "output_compression", label: "Output Compression" },
785
- { key: "moderation", label: "Moderation" },
786
- { key: "partial_images", label: "Partial Images" },
787
- { key: "output_mime_type", label: "Output MIME Type" },
788
- { key: "person_generation", label: "Person Generation" },
789
- { key: "image_format", label: "Image Format" },
790
- { key: "style", label: "Style" },
791
- { key: "reference_strength", label: "Reference Strength" },
792
- { key: "duration_seconds", label: "Duration (s)" },
793
- { key: "duration", label: "Duration" },
794
- { key: "encode_quality", label: "Encode Quality" },
795
- { key: "video_action", label: "Video Action" },
796
- { key: "reference_images", label: "Reference Images" },
797
- { key: "frame_images", label: "Frame Images" },
798
- { key: "image_loras", label: "Image LoRAs" }
799
- ];
800
- var AUDIO_GROUP = [
801
- { key: "tts_voice", label: "Voice" },
802
- { key: "performance_direction", label: "Performance Direction" },
803
- { key: "speech_speed", label: "Speed" },
804
- { key: "turn_pause_ms", label: "Pause Between Turns (ms)" },
805
- { key: "language_code", label: "Language" },
806
- { key: "audio_format", label: "Audio Format" }
807
- ];
808
- var SETTINGS_CATALOGUE = [
809
- { id: "text", label: "", entries: TEXT_GROUP },
810
- { id: "featureFlags", label: "Feature Flags", entries: FEATURE_FLAGS_GROUP },
811
- { id: "imageVideo", label: "Image / Video Settings", entries: IMAGE_VIDEO_GROUP },
812
- { id: "audio", label: "Audio Settings", entries: AUDIO_GROUP }
813
- ];
814
- var CATALOGUE_KEYS = new Set(
815
- SETTINGS_CATALOGUE.flatMap((g) => g.entries.map((e) => e.key))
816
- );
817
- var CATCHALL_EXCLUDED = /* @__PURE__ */ new Set([
818
- "rawControls",
819
- "unmappedControls",
820
- "model_id",
821
- "model",
822
- // chosen via the dedicated model picker, never a generic settings row
823
- "multi_speaker",
824
- // edited via the Voice editor alongside tts_voice
825
- // Model-gated UI flags live in the ui_gates column + UiGatesEditor, never as
826
- // settings rows — even when the model declares them as controls (e.g. every
827
- // model now declares `tools` for the tool-support capability).
828
- ...UI_GATE_KEYS
829
- ]);
830
- function lookupControl(controls, key) {
831
- if (!controls) return null;
832
- if (CATCHALL_EXCLUDED.has(key)) {
833
- if (key === "rawControls" || key === "unmappedControls") return null;
2812
+ function WarningIcon({ className }) {
2813
+ return /* @__PURE__ */ jsxs7("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className, children: [
2814
+ /* @__PURE__ */ jsx8("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
2815
+ /* @__PURE__ */ jsx8("path", { d: "M12 9v4" }),
2816
+ /* @__PURE__ */ jsx8("path", { d: "M12 17h.01" })
2817
+ ] });
2818
+ }
2819
+ function flattenTypeObject(value) {
2820
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && "type" in value && Object.keys(value).length === 1) {
2821
+ return value.type;
2822
+ }
2823
+ return value;
2824
+ }
2825
+ function SettingControlInput({
2826
+ settingKey,
2827
+ control,
2828
+ value,
2829
+ onChange,
2830
+ disabled = false,
2831
+ id
2832
+ }) {
2833
+ const inputId = id ?? `setting-control-${settingKey}`;
2834
+ if (control.type === "enum" && control.enum?.length) {
2835
+ const flattened = settingKey === "response_format" ? flattenTypeObject(value) : value;
2836
+ const stringValue = flattened === void 0 || flattened === null ? "" : String(flattened);
2837
+ const isValueMismatch = stringValue !== "" && !control.enum.includes(stringValue);
2838
+ const emit = (v) => onChange(settingKey === "response_format" ? { type: v } : v);
2839
+ const choice = choiceFor(control.enum);
2840
+ const picker = choice === "aspect-ratio" ? /* @__PURE__ */ jsx8(
2841
+ AspectRatioSelect,
2842
+ {
2843
+ value: isValueMismatch ? "" : stringValue,
2844
+ onChange: emit,
2845
+ options: control.enum,
2846
+ variant: "inline",
2847
+ disabled,
2848
+ id: inputId,
2849
+ placeholder: "Default"
2850
+ }
2851
+ ) : choice === "searchable" ? /* @__PURE__ */ jsx8(
2852
+ OptionCombobox,
2853
+ {
2854
+ value: isValueMismatch ? "" : stringValue,
2855
+ onChange: emit,
2856
+ options: control.enum,
2857
+ variant: "inline",
2858
+ disabled,
2859
+ id: inputId,
2860
+ placeholder: "Default"
2861
+ }
2862
+ ) : null;
2863
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-1 items-center gap-1.5", children: [
2864
+ picker ?? /* @__PURE__ */ jsxs7(
2865
+ Select,
2866
+ {
2867
+ value: isValueMismatch ? "" : stringValue,
2868
+ onValueChange: emit,
2869
+ disabled,
2870
+ children: [
2871
+ /* @__PURE__ */ jsx8(
2872
+ SelectTrigger,
2873
+ {
2874
+ id: inputId,
2875
+ className: "h-7 flex-1 border-0 bg-transparent px-1 text-xs font-medium text-foreground shadow-none hover:bg-transparent focus:ring-0 pointer-coarse:h-11 [&_svg]:h-3 [&_svg]:w-3",
2876
+ children: /* @__PURE__ */ jsx8(SelectValue, { placeholder: isValueMismatch ? stringValue : "Default" })
2877
+ }
2878
+ ),
2879
+ /* @__PURE__ */ jsx8(SelectContent, { className: "text-xs", children: control.enum.map((option) => /* @__PURE__ */ jsx8(
2880
+ SelectItem,
2881
+ {
2882
+ value: option,
2883
+ className: "py-1 text-xs",
2884
+ children: option === ENUM_OFF_VALUE ? "Off" : option
2885
+ },
2886
+ option
2887
+ )) })
2888
+ ]
2889
+ }
2890
+ ),
2891
+ isValueMismatch && /* @__PURE__ */ jsx8(TooltipProvider2, { delayDuration: 200, children: /* @__PURE__ */ jsxs7(Tooltip2, { children: [
2892
+ /* @__PURE__ */ jsx8(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx8("span", { className: "shrink-0 cursor-help text-amber-500", children: /* @__PURE__ */ jsx8(WarningIcon, { className: "h-3.5 w-3.5" }) }) }),
2893
+ /* @__PURE__ */ jsxs7(TooltipContent2, { side: "top", className: "max-w-[220px] type-secondary", children: [
2894
+ '"',
2895
+ stringValue,
2896
+ '" is not a recognized option for this model'
2897
+ ] })
2898
+ ] }) })
2899
+ ] });
834
2900
  }
835
- const candidate = controls[key];
836
- if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
837
- const c = candidate;
838
- if (isControlDefinitionShape(c)) {
839
- return c;
840
- }
2901
+ if (control.type === "boolean") {
2902
+ return /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
2903
+ /* @__PURE__ */ jsx8(
2904
+ Checkbox,
2905
+ {
2906
+ id: inputId,
2907
+ checked: value === true,
2908
+ onCheckedChange: (c) => onChange(c === true),
2909
+ disabled,
2910
+ className: "cursor-pointer"
2911
+ }
2912
+ ),
2913
+ /* @__PURE__ */ jsx8(
2914
+ Label2,
2915
+ {
2916
+ htmlFor: inputId,
2917
+ className: "cursor-pointer text-xs text-muted-foreground",
2918
+ children: value === true ? "On" : "Off"
2919
+ }
2920
+ )
2921
+ ] });
841
2922
  }
842
- return null;
2923
+ if (control.type === "number" || control.type === "integer") {
2924
+ const numeric = typeof value === "number" ? value : void 0;
2925
+ const hasRange = control.min !== void 0 && control.max !== void 0;
2926
+ return /* @__PURE__ */ jsx8(
2927
+ NumberInput,
2928
+ {
2929
+ value: numeric,
2930
+ placeholderValue: typeof control.default === "number" ? control.default : void 0,
2931
+ onChange,
2932
+ onSliderChange: hasRange ? onChange : void 0,
2933
+ min: control.min,
2934
+ max: control.max,
2935
+ step: control.type === "integer" ? 1 : 0.01,
2936
+ isInteger: control.type === "integer",
2937
+ disabled,
2938
+ withSlider: hasRange
2939
+ }
2940
+ );
2941
+ }
2942
+ if (control.type === "string_array") {
2943
+ const arrayValue = Array.isArray(value) ? value.join("\n") : "";
2944
+ return /* @__PURE__ */ jsx8(
2945
+ Textarea,
2946
+ {
2947
+ id: inputId,
2948
+ value: arrayValue,
2949
+ onChange: (e) => onChange(e.target.value.split("\n").filter((s) => s.trim())),
2950
+ disabled,
2951
+ className: "min-h-14 px-2 py-1.5 font-mono text-base leading-snug disabled:opacity-50 sm:text-xs",
2952
+ placeholder: "One value per line..."
2953
+ }
2954
+ );
2955
+ }
2956
+ if (control.type === "array" || control.type === "object_array" || typeof value === "object" && value !== null) {
2957
+ return /* @__PURE__ */ jsx8(
2958
+ JsonValueInput,
2959
+ {
2960
+ id: inputId,
2961
+ value,
2962
+ onChange,
2963
+ disabled
2964
+ }
2965
+ );
2966
+ }
2967
+ return /* @__PURE__ */ jsx8(
2968
+ Input2,
2969
+ {
2970
+ id: inputId,
2971
+ type: "text",
2972
+ value: value === void 0 || value === null ? "" : String(value),
2973
+ onChange: (e) => onChange(e.target.value),
2974
+ disabled,
2975
+ className: "h-7 w-full px-2 text-base pointer-coarse:h-11 sm:text-xs"
2976
+ }
2977
+ );
843
2978
  }
844
- function hasMeaningfulValue(settings, key) {
845
- if (!settings) return false;
846
- const v = settings[key];
847
- return v !== void 0 && v !== null;
2979
+ function JsonValueInput({
2980
+ id,
2981
+ value,
2982
+ onChange,
2983
+ disabled
2984
+ }) {
2985
+ const toText = (v) => v === void 0 || v === null ? "" : JSON.stringify(v, null, 2);
2986
+ const [draft, setDraft] = useState2(() => toText(value));
2987
+ const [jsonError, setJsonError] = useState2(null);
2988
+ useEffect3(() => {
2989
+ setDraft(toText(value));
2990
+ setJsonError(null);
2991
+ }, [JSON.stringify(value)]);
2992
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [
2993
+ /* @__PURE__ */ jsx8(
2994
+ Textarea,
2995
+ {
2996
+ id,
2997
+ value: draft,
2998
+ onChange: (e) => {
2999
+ setDraft(e.target.value);
3000
+ try {
3001
+ JSON.parse(e.target.value);
3002
+ setJsonError(null);
3003
+ } catch (err) {
3004
+ setJsonError(err instanceof Error ? err.message : "Invalid JSON");
3005
+ }
3006
+ },
3007
+ onBlur: (e) => {
3008
+ if (e.target.value.trim() === "") return;
3009
+ try {
3010
+ onChange(JSON.parse(e.target.value));
3011
+ setJsonError(null);
3012
+ } catch {
3013
+ }
3014
+ },
3015
+ disabled,
3016
+ className: "min-h-12 px-2 py-1.5 font-mono text-base leading-snug sm:text-xs",
3017
+ spellCheck: false
3018
+ }
3019
+ ),
3020
+ jsonError && /* @__PURE__ */ jsx8("p", { className: "type-meta text-amber-600 dark:text-amber-400", children: jsonError })
3021
+ ] });
848
3022
  }
849
- function buildSettingsRows(controls, settings) {
850
- const groups = SETTINGS_CATALOGUE.map((group) => ({
851
- id: group.id,
852
- label: group.label,
853
- rows: group.entries.flatMap((entry) => {
854
- const control = lookupControl(controls, entry.key);
855
- if (!control) return [];
856
- return [
857
- {
858
- key: entry.key,
859
- label: entry.label,
860
- group: group.id,
861
- control,
862
- supported: true,
863
- hasValue: hasMeaningfulValue(settings, entry.key)
864
- }
865
- ];
866
- })
3023
+ function NumberInput({
3024
+ value,
3025
+ placeholderValue,
3026
+ onChange,
3027
+ onSliderChange,
3028
+ min,
3029
+ max,
3030
+ step = 1,
3031
+ isInteger = false,
3032
+ disabled = false,
3033
+ withSlider = false
3034
+ }) {
3035
+ const boxClass = "h-7 px-2 text-xs tabular-nums pointer-coarse:h-11";
3036
+ const [draftState, setDraftState] = useState2(() => ({
3037
+ sourceValue: value,
3038
+ text: value === void 0 ? "" : String(value)
867
3039
  }));
868
- const declaredExtra = [];
869
- if (controls) {
870
- for (const key of Object.keys(controls)) {
871
- if (CATALOGUE_KEYS.has(key) || CATCHALL_EXCLUDED.has(key)) continue;
872
- if (lookupControl(controls, key)) declaredExtra.push(key);
873
- }
874
- }
875
- if (declaredExtra.length > 0) {
876
- const rows = declaredExtra.sort().map((key) => ({
877
- key,
878
- label: humanizeIdentifier(key),
879
- group: "other",
880
- control: lookupControl(controls, key),
881
- supported: true,
882
- hasValue: hasMeaningfulValue(settings, key)
883
- }));
884
- groups.push({ id: "other", label: "Other Settings", rows });
3040
+ const shown = value === void 0 ? "" : String(value);
3041
+ const draft = draftState.sourceValue === value ? draftState.text : shown;
3042
+ const unset = value === void 0;
3043
+ const hint = placeholderValue === void 0 ? "Default" : String(placeholderValue);
3044
+ const setDraft = (text) => setDraftState({ sourceValue: value, text });
3045
+ const sliderStep = isInteger ? Math.max(1, Math.trunc(step)) : step;
3046
+ const commit = (raw) => {
3047
+ if (raw === "" || raw === "-") return;
3048
+ const parsed = isInteger ? parseInt(raw, 10) : parseFloat(raw);
3049
+ if (!isNaN(parsed)) {
3050
+ if (parsed !== value) onChange(parsed);
3051
+ } else setDraft(shown);
3052
+ };
3053
+ if (withSlider) {
3054
+ return /* @__PURE__ */ jsxs7(
3055
+ "div",
3056
+ {
3057
+ className: "grid grid-cols-[1fr_4.5rem] items-center gap-2",
3058
+ children: [
3059
+ /* @__PURE__ */ jsx8(
3060
+ Slider,
3061
+ {
3062
+ ...min !== void 0 ? { min } : {},
3063
+ ...max !== void 0 ? { max } : {},
3064
+ step: sliderStep,
3065
+ value: [value ?? placeholderValue ?? min ?? 0],
3066
+ "data-unset": unset ? "true" : void 0,
3067
+ className: unset ? "opacity-50" : void 0,
3068
+ onValueChange: (val) => {
3069
+ const first = val[0] ?? value ?? placeholderValue ?? min ?? 0;
3070
+ const nextValue = isInteger ? Math.trunc(first) : first;
3071
+ onSliderChange?.(nextValue);
3072
+ setDraft(String(nextValue));
3073
+ },
3074
+ disabled
3075
+ }
3076
+ ),
3077
+ /* @__PURE__ */ jsx8(
3078
+ Input2,
3079
+ {
3080
+ type: "text",
3081
+ inputMode: "decimal",
3082
+ value: draft,
3083
+ placeholder: hint,
3084
+ onChange: (e) => setDraft(e.target.value),
3085
+ onBlur: (e) => commit(e.target.value),
3086
+ onFocus: (e) => e.target.select(),
3087
+ onKeyDown: (e) => {
3088
+ if (e.key === "Enter") e.currentTarget.blur();
3089
+ },
3090
+ disabled,
3091
+ className: boxClass
3092
+ }
3093
+ )
3094
+ ]
3095
+ }
3096
+ );
885
3097
  }
886
- return groups;
3098
+ return /* @__PURE__ */ jsx8(
3099
+ Input2,
3100
+ {
3101
+ type: "text",
3102
+ inputMode: "decimal",
3103
+ value: draft,
3104
+ placeholder: hint,
3105
+ onChange: (e) => setDraft(e.target.value),
3106
+ onBlur: (e) => commit(e.target.value),
3107
+ onFocus: (e) => e.target.select(),
3108
+ onKeyDown: (e) => {
3109
+ if (e.key === "Enter") e.currentTarget.blur();
3110
+ },
3111
+ disabled,
3112
+ className: `${boxClass} w-full`
3113
+ }
3114
+ );
887
3115
  }
888
3116
 
889
3117
  // settings/react/ModelSettingsPanel.tsx
890
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3118
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
891
3119
  function StatusGlyph({ issue, source }) {
892
3120
  const title = issue ? issue.message : source ? `Valid \xB7 ${source}` : "Valid";
893
- return /* @__PURE__ */ jsx5(
3121
+ return /* @__PURE__ */ jsx9(
894
3122
  "span",
895
3123
  {
896
- className: cn("inline-flex h-7 w-6 shrink-0 items-center justify-center", issue ? "text-warning" : "text-success"),
3124
+ className: cn2("inline-flex h-7 w-6 shrink-0 items-center justify-center", issue ? "text-warning" : "text-success"),
897
3125
  role: "img",
898
3126
  "aria-label": title,
899
3127
  title,
900
- children: issue ? /* @__PURE__ */ jsx5(TriangleAlertGlyph, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx5(CircleCheckGlyph, { className: "h-3.5 w-3.5" })
3128
+ children: issue ? /* @__PURE__ */ jsx9(TriangleAlertGlyph, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx9(CircleCheckGlyph, { className: "h-3.5 w-3.5" })
901
3129
  }
902
3130
  );
903
3131
  }
904
3132
  function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
905
3133
  const read = target.read(row.key, control);
906
3134
  const resetLabel = target.resetLabel ?? "Reset to agent";
907
- const reset = read.changedHere && target.resetToInherited ? /* @__PURE__ */ jsx5(
3135
+ const reset = read.changedHere && target.resetToInherited ? /* @__PURE__ */ jsx9(
908
3136
  Button2,
909
3137
  {
910
3138
  variant: "quiet",
911
- icon: /* @__PURE__ */ jsx5(RotateCcwGlyph, {}),
3139
+ icon: /* @__PURE__ */ jsx9(RotateCcwGlyph, {}),
912
3140
  onClick: () => target.resetToInherited?.(row.key),
913
3141
  disabled,
914
3142
  "aria-label": `${resetLabel}: ${row.label}`,
@@ -917,20 +3145,20 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
917
3145
  }
918
3146
  ) : null;
919
3147
  if (read.state === "bound") {
920
- return /* @__PURE__ */ jsx5(
3148
+ return /* @__PURE__ */ jsx9(
921
3149
  SettingRowFrame,
922
3150
  {
923
3151
  rowKey: row.key,
924
3152
  state: "bound",
925
- leading: /* @__PURE__ */ jsx5(Checkbox2, { checked: true, disabled: true, "aria-label": `${row.label} is a run input` }),
926
- label: /* @__PURE__ */ jsx5(RowLabel, { children: row.label }),
927
- actions: /* @__PURE__ */ jsxs5(Fragment2, { children: [
3153
+ leading: /* @__PURE__ */ jsx9(Checkbox2, { checked: true, disabled: true, "aria-label": `${row.label} is a run input` }),
3154
+ label: /* @__PURE__ */ jsx9(RowLabel, { children: row.label }),
3155
+ actions: /* @__PURE__ */ jsxs8(Fragment4, { children: [
928
3156
  reset,
929
- target.unbind ? /* @__PURE__ */ jsx5(
3157
+ target.unbind ? /* @__PURE__ */ jsx9(
930
3158
  Button2,
931
3159
  {
932
3160
  variant: "quiet",
933
- icon: /* @__PURE__ */ jsx5(UnlinkGlyph, {}),
3161
+ icon: /* @__PURE__ */ jsx9(UnlinkGlyph, {}),
934
3162
  onClick: () => target.unbind?.(row.key, control),
935
3163
  disabled,
936
3164
  "aria-label": `Make ${row.label} a fixed setting`,
@@ -938,9 +3166,9 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
938
3166
  }
939
3167
  ) : null
940
3168
  ] }),
941
- children: /* @__PURE__ */ jsxs5("span", { className: "flex min-h-7 min-w-0 items-center gap-2", "data-bound-control": row.key, children: [
942
- /* @__PURE__ */ jsx5(Chip, { tone: "primary", icon: /* @__PURE__ */ jsx5(VariableGlyph, {}), label: `{{${read.variableName}}}` }),
943
- /* @__PURE__ */ jsx5("span", { className: "truncate type-meta text-muted-foreground", children: read.value === void 0 || read.value === null || read.value === "" ? "Model default" : formatSettingValue(read.value) })
3169
+ children: /* @__PURE__ */ jsxs8("span", { className: "flex min-h-7 min-w-0 items-center gap-2", "data-bound-control": row.key, children: [
3170
+ /* @__PURE__ */ jsx9(Chip, { tone: "primary", icon: /* @__PURE__ */ jsx9(VariableGlyph, {}), label: `{{${read.variableName}}}` }),
3171
+ /* @__PURE__ */ jsx9("span", { className: "truncate type-meta text-muted-foreground", children: read.value === void 0 || read.value === null || read.value === "" ? "Model default" : formatSettingValue(read.value) })
944
3172
  ] })
945
3173
  }
946
3174
  );
@@ -950,12 +3178,12 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
950
3178
  const isOff = isSet && isOffValue(value, control);
951
3179
  const checkboxId = `${idPrefix}-setting-${row.key}`;
952
3180
  const canBind = Boolean(target.bind) && (target.canBind?.(row.key) ?? true);
953
- return /* @__PURE__ */ jsx5(
3181
+ return /* @__PURE__ */ jsx9(
954
3182
  SettingRowFrame,
955
3183
  {
956
3184
  rowKey: row.key,
957
3185
  state: isOff ? "off" : read.state,
958
- leading: /* @__PURE__ */ jsx5(
3186
+ leading: /* @__PURE__ */ jsx9(
959
3187
  Checkbox2,
960
3188
  {
961
3189
  id: checkboxId,
@@ -964,14 +3192,14 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
964
3192
  onCheckedChange: (next) => next === true ? target.turnOn(row.key, control) : target.clear(row.key)
965
3193
  }
966
3194
  ),
967
- label: /* @__PURE__ */ jsx5(RowLabel, { htmlFor: checkboxId, muted: !isSet, children: row.label }),
968
- actions: /* @__PURE__ */ jsxs5(Fragment2, { children: [
969
- /* @__PURE__ */ jsx5(StatusGlyph, { issue: read.issue, source: read.source }),
970
- canBind ? /* @__PURE__ */ jsx5(
3195
+ label: /* @__PURE__ */ jsx9(RowLabel, { htmlFor: checkboxId, muted: !isSet, children: row.label }),
3196
+ actions: /* @__PURE__ */ jsxs8(Fragment4, { children: [
3197
+ /* @__PURE__ */ jsx9(StatusGlyph, { issue: read.issue, source: read.source }),
3198
+ canBind ? /* @__PURE__ */ jsx9(
971
3199
  Button2,
972
3200
  {
973
3201
  variant: "quiet",
974
- icon: /* @__PURE__ */ jsx5(VariableGlyph, {}),
3202
+ icon: /* @__PURE__ */ jsx9(VariableGlyph, {}),
975
3203
  onClick: () => target.bind?.(row.key, control),
976
3204
  disabled,
977
3205
  "aria-label": `Make ${row.label} a run input`,
@@ -979,11 +3207,11 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
979
3207
  "data-bind-control": row.key
980
3208
  }
981
3209
  ) : null,
982
- isSet && read.issue?.fixable && target.fix ? /* @__PURE__ */ jsx5(
3210
+ isSet && read.issue?.fixable && target.fix ? /* @__PURE__ */ jsx9(
983
3211
  Button2,
984
3212
  {
985
3213
  variant: "quiet",
986
- icon: /* @__PURE__ */ jsx5(WrenchGlyph, {}),
3214
+ icon: /* @__PURE__ */ jsx9(WrenchGlyph, {}),
987
3215
  onClick: () => target.fix?.(row.key),
988
3216
  disabled,
989
3217
  "aria-label": `Fix ${row.label} automatically`,
@@ -991,12 +3219,12 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
991
3219
  }
992
3220
  ) : null,
993
3221
  reset,
994
- isSet ? /* @__PURE__ */ jsx5(
3222
+ isSet ? /* @__PURE__ */ jsx9(
995
3223
  Button2,
996
3224
  {
997
3225
  variant: "quiet",
998
3226
  removes: true,
999
- icon: /* @__PURE__ */ jsx5(Trash2Glyph, {}),
3227
+ icon: /* @__PURE__ */ jsx9(Trash2Glyph, {}),
1000
3228
  onClick: () => target.clear(row.key),
1001
3229
  disabled,
1002
3230
  "aria-label": `Clear ${row.label} to not set`,
@@ -1005,8 +3233,8 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
1005
3233
  }
1006
3234
  ) : null
1007
3235
  ] }),
1008
- children: isSet ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1009
- /* @__PURE__ */ jsx5(
3236
+ children: isSet ? /* @__PURE__ */ jsxs8(Fragment4, { children: [
3237
+ /* @__PURE__ */ jsx9(
1010
3238
  SettingControlInput,
1011
3239
  {
1012
3240
  settingKey: row.key,
@@ -1017,23 +3245,77 @@ function ModelSettingRow({ row, control, target, idPrefix, disabled }) {
1017
3245
  id: `${idPrefix}-control-${row.key}`
1018
3246
  }
1019
3247
  ),
1020
- read.issue ? /* @__PURE__ */ jsx5("p", { className: "pt-1 type-meta text-warning-ink", children: read.issue.message }) : null
1021
- ] }) : /* @__PURE__ */ jsx5(RowText, { children: read.modelDefault !== void 0 && read.modelDefault !== null ? `Not set \xB7 Model default: ${formatSettingValue(read.modelDefault)}` : "Not set" })
3248
+ read.issue ? /* @__PURE__ */ jsx9("p", { className: "pt-1 type-meta text-warning-ink", children: read.issue.message }) : null
3249
+ ] }) : /* @__PURE__ */ jsx9(RowText, { children: read.modelDefault !== void 0 && read.modelDefault !== null ? `Not set \xB7 Model default: ${formatSettingValue(read.modelDefault)}` : "Not set" })
1022
3250
  }
1023
3251
  );
1024
3252
  }
1025
3253
  function ExtraSettingRow({ rowKey, label, value, kind, onClear, onReset, resetLabel = "Reset to agent", disabled = false }) {
1026
- return /* @__PURE__ */ jsx5(
3254
+ return /* @__PURE__ */ jsx9(
1027
3255
  SettingRowFrame,
1028
3256
  {
1029
3257
  rowKey,
1030
3258
  state: kind,
1031
- label: /* @__PURE__ */ jsx5(RowLabel, { children: label }),
1032
- actions: /* @__PURE__ */ jsxs5(Fragment2, { children: [
1033
- kind === "translated" ? /* @__PURE__ */ jsx5(Chip, { tone: "sky", label: "Translated", title: "Kept as set and sent as this model's closest equivalent" }) : /* @__PURE__ */ jsx5(Chip, { tone: "warning", label: "Not on this model", title: "This model has no such setting" }),
1034
- onReset ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx5(RotateCcwGlyph, {}), onClick: onReset, disabled, "aria-label": `${resetLabel}: ${label}`, title: resetLabel }) : /* @__PURE__ */ jsx5(Button2, { variant: "quiet", removes: true, icon: /* @__PURE__ */ jsx5(Trash2Glyph, {}), onClick: onClear, disabled, "aria-label": `Clear ${label}`, title: "Clear to not set" })
3259
+ label: /* @__PURE__ */ jsx9(RowLabel, { children: label }),
3260
+ actions: /* @__PURE__ */ jsxs8(Fragment4, { children: [
3261
+ kind === "translated" ? /* @__PURE__ */ jsx9(Chip, { tone: "sky", label: "Translated", title: "Kept as set and sent as this model's closest equivalent" }) : /* @__PURE__ */ jsx9(Chip, { tone: "warning", label: "Not on this model", title: "This model has no such setting" }),
3262
+ onReset ? /* @__PURE__ */ jsx9(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx9(RotateCcwGlyph, {}), onClick: onReset, disabled, "aria-label": `${resetLabel}: ${label}`, title: resetLabel, "data-setting-reset": rowKey }) : /* @__PURE__ */ jsx9(Button2, { variant: "quiet", removes: true, icon: /* @__PURE__ */ jsx9(Trash2Glyph, {}), onClick: onClear, disabled, "aria-label": `Clear ${label}`, title: "Clear to not set", "data-setting-clear": rowKey })
1035
3263
  ] }),
1036
- children: /* @__PURE__ */ jsx5(RowText, { mono: true, children: isOffValue(value, null) ? "Off" : formatSettingValue(value) })
3264
+ children: /* @__PURE__ */ jsx9(RowText, { mono: value !== void 0 && value !== null, children: value === void 0 || value === null ? "Not set" : isOffValue(value, null) ? "Off" : formatSettingValue(value) })
3265
+ }
3266
+ );
3267
+ }
3268
+ function ModelRow2({ model, resetLabel, idPrefix, disabled }) {
3269
+ const off = disabled || model.disabled === true;
3270
+ const oneMessageId = `${idPrefix}-model-one-message`;
3271
+ return /* @__PURE__ */ jsx9(
3272
+ SettingRowFrame,
3273
+ {
3274
+ rowKey: "model",
3275
+ state: model.value ? "set" : "unset",
3276
+ label: /* @__PURE__ */ jsx9(RowLabel, { children: "Model" }),
3277
+ actions: model.changedHere && model.resetToInherited ? /* @__PURE__ */ jsx9(
3278
+ Button2,
3279
+ {
3280
+ variant: "quiet",
3281
+ icon: /* @__PURE__ */ jsx9(RotateCcwGlyph, {}),
3282
+ onClick: () => model.resetToInherited?.(),
3283
+ disabled: off,
3284
+ "aria-label": `${resetLabel}: Model`,
3285
+ title: resetLabel,
3286
+ "data-setting-reset": "model"
3287
+ }
3288
+ ) : null,
3289
+ children: /* @__PURE__ */ jsxs8("div", { className: "flex min-h-7 min-w-0 flex-wrap items-center gap-x-2", children: [
3290
+ /* @__PURE__ */ jsx9(
3291
+ ModelListDropdown,
3292
+ {
3293
+ "aria-label": "Model",
3294
+ value: model.value,
3295
+ onValueChange: model.change,
3296
+ inputModalities: [],
3297
+ disabled: off,
3298
+ className: "min-w-0",
3299
+ pinnedOfferingId: model.pinnedOfferingId,
3300
+ onOfferingPinChange: model.changePin,
3301
+ ...model.emptyChoice ? { emptyOptionLabel: model.emptyChoice.label, onClear: model.emptyChoice.choose, placeholder: model.emptyChoice.label } : {},
3302
+ ...model.localModels ? { localModels: model.localModels } : {},
3303
+ ...model.disabledTitle ? { disabledTitle: model.disabledTitle } : {}
3304
+ }
3305
+ ),
3306
+ model.oneMessage ? /* @__PURE__ */ jsxs8("label", { htmlFor: oneMessageId, className: "flex min-h-7 items-center gap-1 type-secondary text-muted-foreground", title: "Use this model for the next message, then go back", children: [
3307
+ /* @__PURE__ */ jsx9(
3308
+ Switch2,
3309
+ {
3310
+ id: oneMessageId,
3311
+ checked: model.oneMessage.on,
3312
+ onCheckedChange: (next) => model.oneMessage?.change(next),
3313
+ disabled
3314
+ }
3315
+ ),
3316
+ "This message only"
3317
+ ] }) : null
3318
+ ] })
1037
3319
  }
1038
3320
  );
1039
3321
  }
@@ -1050,15 +3332,33 @@ function ModelSettingsPanel({
1050
3332
  disabled = false
1051
3333
  }) {
1052
3334
  const groups = controls ? buildSettingsRows(controls, values).filter((group) => group.rows.length > 0) : [];
1053
- return /* @__PURE__ */ jsxs5("div", { "data-model-settings-panel": "", className: "@container flex min-w-0 flex-col", children: [
1054
- model ? /* @__PURE__ */ jsx5(SettingRowFrame, { rowKey: "model", label: /* @__PURE__ */ jsx5(RowLabel, { children: "Model" }), children: model }) : null,
3335
+ const extras = extraSettingKeys(controls, values, target.changedKeys);
3336
+ const resetLabel = target.resetLabel ?? "Reset to agent";
3337
+ const extraRow = (key, kind) => /* @__PURE__ */ jsx9(
3338
+ ExtraSettingRow,
3339
+ {
3340
+ rowKey: key,
3341
+ label: settingLabel(key),
3342
+ value: values?.[key],
3343
+ kind,
3344
+ onClear: () => target.clear(key),
3345
+ onReset: target.resetToInherited && target.read(key, null).changedHere ? () => target.resetToInherited?.(key) : void 0,
3346
+ resetLabel,
3347
+ disabled
3348
+ },
3349
+ key
3350
+ );
3351
+ return /* @__PURE__ */ jsxs8("div", { "data-model-settings-panel": "", className: "@container flex min-w-0 flex-col", children: [
3352
+ model ? /* @__PURE__ */ jsx9(ModelRow2, { model, resetLabel, idPrefix, disabled }) : null,
1055
3353
  notices,
1056
- groups.length === 0 && emptyNote ? /* @__PURE__ */ jsx5("div", { className: "py-2 type-secondary text-muted-foreground", children: emptyNote }) : null,
1057
- groups.map((group) => /* @__PURE__ */ jsx5(SettingsGroupSection, { id: group.id, title: group.label || void 0, children: group.rows.map((row) => {
3354
+ extras.unsupported.map((key) => extraRow(key, "unsupported")),
3355
+ extras.translated.length > 0 ? /* @__PURE__ */ jsx9(SettingsGroupSection, { id: "translated", title: "Translated for this model", children: /* @__PURE__ */ jsx9("div", { "data-translated-settings": "", children: extras.translated.map((key) => extraRow(key, "translated")) }) }) : null,
3356
+ groups.length === 0 && emptyNote ? /* @__PURE__ */ jsx9("div", { className: "py-2 type-secondary text-muted-foreground", children: emptyNote }) : null,
3357
+ groups.map((group) => /* @__PURE__ */ jsx9(SettingsGroupSection, { id: group.id, title: group.label || void 0, children: group.rows.map((row) => {
1058
3358
  const custom = renderRow?.(row);
1059
- if (custom !== void 0) return /* @__PURE__ */ jsx5("div", { children: custom }, row.key);
3359
+ if (custom !== void 0) return /* @__PURE__ */ jsx9("div", { children: custom }, row.key);
1060
3360
  if (!row.control) return null;
1061
- return /* @__PURE__ */ jsx5(
3361
+ return /* @__PURE__ */ jsx9(
1062
3362
  ModelSettingRow,
1063
3363
  {
1064
3364
  row,
@@ -1070,14 +3370,14 @@ function ModelSettingsPanel({
1070
3370
  row.key
1071
3371
  );
1072
3372
  }) }, group.id)),
1073
- capabilities ? /* @__PURE__ */ jsx5(
3373
+ capabilities ? /* @__PURE__ */ jsx9(
1074
3374
  InputCapabilitiesEditor,
1075
3375
  {
1076
3376
  values: capabilities.values,
1077
3377
  onChange: capabilities.change,
1078
3378
  changedKeys: capabilities.changedKeys,
1079
3379
  onReset: capabilities.resetToInherited,
1080
- resetLabel: target.resetLabel,
3380
+ resetLabel,
1081
3381
  error: capabilities.error,
1082
3382
  idPrefix: `${idPrefix}-capability`,
1083
3383
  disabled
@@ -1088,7 +3388,7 @@ function ModelSettingsPanel({
1088
3388
 
1089
3389
  // settings/react/index.ts
1090
3390
  function useSettingsSelector(store, select) {
1091
- return useSyncExternalStore(
3391
+ return useSyncExternalStore2(
1092
3392
  store.subscribe,
1093
3393
  () => select(store.getState()),
1094
3394
  () => select(store.getState())
@@ -1112,13 +3412,9 @@ function memoEffective(entry) {
1112
3412
  return hit;
1113
3413
  }
1114
3414
  export {
1115
- ExtraSettingRow,
1116
- InputCapabilitiesEditor,
1117
3415
  ModelSettingsPanel,
1118
- SettingControlInput,
1119
3416
  SettingRowFrame,
1120
3417
  RowLabel as SettingRowLabel,
1121
- SettingsGroupSection,
1122
3418
  formatSettingValue,
1123
3419
  useEffectiveSettings,
1124
3420
  useSettingsEntry,