@ai-matrx/agents 0.59.194 → 0.59.195

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