@tangle-network/sandbox-ui 0.80.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,424 @@
1
+ import {
2
+ BrandLogo,
3
+ ModelBrandStack,
4
+ brandInfo,
5
+ canonicalModelId,
6
+ normalizeBrandKey,
7
+ resolveModelBrandIdentity
8
+ } from "./chunk-I3G23XHN.js";
9
+ import {
10
+ cn
11
+ } from "./chunk-EI44GEQ5.js";
12
+
13
+ // src/dashboard/model-picker.tsx
14
+ import * as React from "react";
15
+ import { ChevronDown, Search, Sparkles, Loader2 } from "lucide-react";
16
+ import * as Popover from "@radix-ui/react-dropdown-menu";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ function modelDedupKey(model) {
19
+ const canonical = canonicalModelId(model).toLowerCase();
20
+ const segments = canonical.split("/").filter(Boolean);
21
+ const modelId = segments[segments.length - 1] ?? canonical;
22
+ const lab = resolveModelBrandIdentity(model).lab.key;
23
+ return lab === "unknown" ? canonical : `${lab}/${modelId}`;
24
+ }
25
+ function dedupeModels(models) {
26
+ const byKey = /* @__PURE__ */ new Map();
27
+ const result = [];
28
+ for (const model of models) {
29
+ const key = modelDedupKey(model);
30
+ const existingIndex = byKey.get(key);
31
+ if (existingIndex === void 0) {
32
+ byKey.set(key, result.length);
33
+ result.push(model);
34
+ continue;
35
+ }
36
+ const existing = result[existingIndex];
37
+ if (!existing.featured && model.featured) {
38
+ result[existingIndex] = model;
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+ var DEFAULT_FEATURED_MODEL_IDS = [
44
+ "anthropic/claude-opus-4-8",
45
+ "anthropic/claude-sonnet-4-6",
46
+ "openai/gpt-5.4",
47
+ "google/gemini-2.5-pro",
48
+ "deepseek/deepseek-v3"
49
+ ];
50
+ function formatPricing(pricing) {
51
+ const prompt = Number(pricing?.prompt ?? 0);
52
+ const completion = Number(pricing?.completion ?? 0);
53
+ if (!prompt && !completion) return null;
54
+ const fmt = (n) => {
55
+ const perM = n * 1e6;
56
+ if (perM === 0) return "0";
57
+ if (perM >= 1) return `$${perM.toFixed(2)}`;
58
+ return `$${perM.toFixed(2)}`;
59
+ };
60
+ return `${fmt(prompt)} / ${fmt(completion)} per 1M`;
61
+ }
62
+ function formatContext(ctx) {
63
+ if (!ctx) return null;
64
+ if (ctx >= 1e6) return `${(ctx / 1e6).toFixed(1)}M ctx`;
65
+ if (ctx >= 1e3) return `${Math.round(ctx / 1e3)}k ctx`;
66
+ return `${ctx} ctx`;
67
+ }
68
+ var PRIORITY_MODEL_GROUPS = ["anthropic", "openai", "google", "deepseek", "zai", "moonshot"];
69
+ function modelGroupKey(model) {
70
+ const identity = resolveModelBrandIdentity(model);
71
+ if (identity.lab.key !== "unknown") return identity.lab.key;
72
+ if (identity.host.key !== "unknown") return identity.host.key;
73
+ return (model._provider ?? model.provider ?? "other").toLowerCase();
74
+ }
75
+ function modelGroupLabel(key) {
76
+ if (key === "moonshot") return "Kimi";
77
+ const brand = brandInfo(normalizeBrandKey(key));
78
+ return brand.key === "unknown" ? key : brand.label;
79
+ }
80
+ function modelGroupRank(key) {
81
+ const priorityIndex = PRIORITY_MODEL_GROUPS.indexOf(normalizeBrandKey(key));
82
+ return priorityIndex === -1 ? Number.POSITIVE_INFINITY : priorityIndex;
83
+ }
84
+ function compareModelGroups(a, b) {
85
+ const aRank = modelGroupRank(a);
86
+ const bRank = modelGroupRank(b);
87
+ if (aRank !== bRank) return aRank - bRank;
88
+ return modelGroupLabel(a).localeCompare(modelGroupLabel(b));
89
+ }
90
+ function compareModelsByDisplayName(a, b) {
91
+ return (a.name ?? a.id).localeCompare(b.name ?? b.id);
92
+ }
93
+ function stripBrandPrefix(name, identity) {
94
+ if (!identity) return name;
95
+ for (const label of [identity.lab.label, identity.host.label]) {
96
+ if (!label) continue;
97
+ if (name.toLowerCase().startsWith(label.toLowerCase())) {
98
+ const after = name.slice(label.length);
99
+ if (/^[\s:]/.test(after)) {
100
+ const rest = after.replace(/^[\s:]+/, "").trim();
101
+ if (rest) return rest;
102
+ }
103
+ }
104
+ }
105
+ return name;
106
+ }
107
+ function ModelPicker({
108
+ value,
109
+ onChange,
110
+ models,
111
+ loading = false,
112
+ recents,
113
+ popular,
114
+ excludeProviders,
115
+ modalities,
116
+ variant = "field",
117
+ label = "Model",
118
+ placeholder = "Choose a model",
119
+ className,
120
+ triggerClassName,
121
+ disabled,
122
+ side,
123
+ avoidCollisions
124
+ }) {
125
+ const [query, setQuery] = React.useState("");
126
+ const [open, setOpen] = React.useState(false);
127
+ const searchInputRef = React.useRef(null);
128
+ React.useLayoutEffect(() => {
129
+ if (!open) return;
130
+ const frame = requestAnimationFrame(() => {
131
+ searchInputRef.current?.focus();
132
+ searchInputRef.current?.select();
133
+ });
134
+ return () => cancelAnimationFrame(frame);
135
+ }, [open]);
136
+ const deduped = React.useMemo(() => dedupeModels(models), [models]);
137
+ const filtered = React.useMemo(() => {
138
+ const excluded = new Set((excludeProviders ?? []).map((p) => p.toLowerCase()));
139
+ const allowedModalities = modalities ? new Set(modalities) : null;
140
+ const q = query.trim().toLowerCase();
141
+ return deduped.filter((m) => {
142
+ const provider = (m._provider ?? m.provider ?? "").toLowerCase();
143
+ if (excluded.has(provider)) return false;
144
+ if (allowedModalities && m.architecture?.modality && !allowedModalities.has(m.architecture.modality)) return false;
145
+ if (!q) return true;
146
+ const id = canonicalModelId(m).toLowerCase();
147
+ const name = (m.name ?? "").toLowerCase();
148
+ const identity = resolveModelBrandIdentity(m);
149
+ return id.includes(q) || name.includes(q) || provider.includes(q) || identity.host.label.toLowerCase().includes(q) || identity.lab.label.toLowerCase().includes(q);
150
+ });
151
+ }, [deduped, query, modalities, excludeProviders]);
152
+ const grouped = React.useMemo(() => {
153
+ const groups = /* @__PURE__ */ new Map();
154
+ for (const m of filtered) {
155
+ const key = modelGroupKey(m);
156
+ const list = groups.get(key);
157
+ if (list) list.push(m);
158
+ else groups.set(key, [m]);
159
+ }
160
+ return Array.from(groups.entries()).map(([key, list]) => [key, [...list].sort(compareModelsByDisplayName)]).sort(([a], [b]) => compareModelGroups(a, b));
161
+ }, [filtered]);
162
+ const current = React.useMemo(
163
+ () => models.find((m) => canonicalModelId(m) === value),
164
+ [models, value]
165
+ );
166
+ const currentIdentity = current ? resolveModelBrandIdentity(current) : null;
167
+ const currentLabel = stripBrandPrefix(
168
+ current?.name ?? current?.id ?? value,
169
+ currentIdentity
170
+ );
171
+ const recentIds = React.useMemo(() => {
172
+ if (!recents?.length) return [];
173
+ const lookup = new Map(models.map((m) => [canonicalModelId(m), m]));
174
+ return recents.map((id) => lookup.get(id)).filter((m) => Boolean(m)).slice(0, 4);
175
+ }, [recents, models]);
176
+ const popularModels = React.useMemo(() => {
177
+ if (!popular?.length) return [];
178
+ const lookup = new Map(models.map((m) => [canonicalModelId(m), m]));
179
+ return popular.map((id) => lookup.get(id)).filter((m) => Boolean(m));
180
+ }, [popular, models]);
181
+ const featuredModels = React.useMemo(() => {
182
+ const flagged = deduped.filter((m) => m.featured);
183
+ if (flagged.length > 0) return flagged;
184
+ const seedKeys = new Set(
185
+ DEFAULT_FEATURED_MODEL_IDS.map((id) => modelDedupKey({ id }))
186
+ );
187
+ const seeded = deduped.filter((m) => seedKeys.has(modelDedupKey(m)));
188
+ const order = DEFAULT_FEATURED_MODEL_IDS.map((id) => modelDedupKey({ id }));
189
+ return seeded.sort(
190
+ (a, b) => order.indexOf(modelDedupKey(a)) - order.indexOf(modelDedupKey(b))
191
+ );
192
+ }, [deduped]);
193
+ const handleSelect = (id) => {
194
+ onChange(id);
195
+ setOpen(false);
196
+ setQuery("");
197
+ };
198
+ const triggerLabel = label ? `${label}: ${currentLabel || placeholder}` : currentLabel || placeholder;
199
+ const trigger = variant === "pill" ? /* @__PURE__ */ jsxs(
200
+ "button",
201
+ {
202
+ type: "button",
203
+ "aria-label": triggerLabel,
204
+ disabled,
205
+ className: cn(
206
+ "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container",
207
+ "px-2.5 text-xs font-medium text-foreground shadow-sm",
208
+ "transition-colors duration-[var(--transition-fast)]",
209
+ "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
210
+ "focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
211
+ "data-[state=open]:border-[var(--md3-outline)] data-[state=open]:bg-surface-container-high",
212
+ "disabled:opacity-50 disabled:cursor-not-allowed",
213
+ className,
214
+ triggerClassName
215
+ ),
216
+ children: [
217
+ currentIdentity ? /* @__PURE__ */ jsx(ModelBrandStack, { identity: currentIdentity, size: "sm" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3 text-muted-foreground" }),
218
+ /* @__PURE__ */ jsx("span", { className: "truncate max-w-[160px]", children: currentLabel || placeholder }),
219
+ /* @__PURE__ */ jsx(ChevronDown, { className: "ml-auto h-3 w-3 shrink-0 text-muted-foreground transition-transform data-[state=open]:rotate-180" })
220
+ ]
221
+ }
222
+ ) : /* @__PURE__ */ jsxs(
223
+ "button",
224
+ {
225
+ type: "button",
226
+ "aria-label": triggerLabel,
227
+ disabled,
228
+ className: cn(
229
+ "flex w-full items-center justify-between gap-2 rounded-[var(--radius-md)]",
230
+ "border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2.5 text-sm text-left",
231
+ "transition-colors duration-[var(--transition-fast)]",
232
+ "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high",
233
+ "focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
234
+ "data-[state=open]:border-[var(--md3-outline)] data-[state=open]:bg-surface-container-high",
235
+ "disabled:opacity-50 disabled:cursor-not-allowed",
236
+ className,
237
+ triggerClassName
238
+ ),
239
+ children: [
240
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
241
+ currentIdentity && /* @__PURE__ */ jsx(ModelBrandStack, { identity: currentIdentity, size: "sm" }),
242
+ /* @__PURE__ */ jsx("span", { className: cn("truncate", current ? "text-foreground font-medium" : "text-muted-foreground"), children: currentLabel || placeholder })
243
+ ] }),
244
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 shrink-0 text-muted-foreground transition-transform data-[state=open]:rotate-180" })
245
+ ]
246
+ }
247
+ );
248
+ return /* @__PURE__ */ jsxs("div", { className: cn(variant === "field" ? "space-y-1.5" : "inline-flex", variant === "field" ? className : void 0), children: [
249
+ variant === "field" && label && /* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-muted-foreground uppercase tracking-[0.06em]", children: label }),
250
+ /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, modal: false, children: [
251
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: trigger }),
252
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
253
+ Popover.Content,
254
+ {
255
+ side,
256
+ sideOffset: 4,
257
+ align: variant === "pill" ? "start" : "start",
258
+ avoidCollisions,
259
+ collisionPadding: 24,
260
+ className: cn(
261
+ "z-50 w-[var(--radix-dropdown-menu-trigger-width)] min-w-[320px] max-w-[460px]",
262
+ // Cap to the smaller of the fixed height and the viewport space on
263
+ // the open side, so a list pinned downward never runs off-screen.
264
+ "max-h-[min(440px,var(--radix-dropdown-menu-content-available-height))] overflow-hidden flex flex-col",
265
+ "rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
266
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
267
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
268
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
269
+ ),
270
+ children: [
271
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-[var(--md3-outline-variant)] bg-surface-container-high px-3 py-2", children: [
272
+ /* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
273
+ /* @__PURE__ */ jsx(
274
+ "input",
275
+ {
276
+ ref: searchInputRef,
277
+ type: "text",
278
+ value: query,
279
+ onChange: (e) => setQuery(e.target.value),
280
+ onKeyDown: (e) => {
281
+ if (e.key.length === 1) e.stopPropagation();
282
+ },
283
+ placeholder: "Search models...",
284
+ autoFocus: true,
285
+ className: "flex-1 bg-transparent text-sm font-medium outline-none placeholder:font-normal placeholder:text-muted-foreground"
286
+ }
287
+ ),
288
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin text-muted-foreground" })
289
+ ] }),
290
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
291
+ !query && featuredModels.length > 0 && /* @__PURE__ */ jsx(Section, { label: "Recommended", tone: "featured", children: featuredModels.map((m) => /* @__PURE__ */ jsx(
292
+ ModelRow,
293
+ {
294
+ model: m,
295
+ active: canonicalModelId(m) === value,
296
+ onSelect: handleSelect,
297
+ featured: true
298
+ },
299
+ `featured-${canonicalModelId(m)}`
300
+ )) }),
301
+ !query && popularModels.length > 0 && /* @__PURE__ */ jsx(Section, { label: "Top models", tone: "featured", children: popularModels.map((m) => /* @__PURE__ */ jsx(
302
+ ModelRow,
303
+ {
304
+ model: m,
305
+ active: canonicalModelId(m) === value,
306
+ onSelect: handleSelect,
307
+ featured: true
308
+ },
309
+ `popular-${canonicalModelId(m)}`
310
+ )) }),
311
+ !query && recentIds.length > 0 && /* @__PURE__ */ jsx(Section, { label: "Recent", children: recentIds.map((m) => /* @__PURE__ */ jsx(
312
+ ModelRow,
313
+ {
314
+ model: m,
315
+ active: canonicalModelId(m) === value,
316
+ onSelect: handleSelect
317
+ },
318
+ `recent-${canonicalModelId(m)}`
319
+ )) }),
320
+ grouped.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: loading ? "Loading models..." : query ? "No models match." : "No models available." }) : grouped.map(([groupKey, list]) => /* @__PURE__ */ jsx(Section, { label: modelGroupLabel(groupKey), children: list.map((m) => /* @__PURE__ */ jsx(
321
+ ModelRow,
322
+ {
323
+ model: m,
324
+ active: canonicalModelId(m) === value,
325
+ onSelect: handleSelect
326
+ },
327
+ canonicalModelId(m)
328
+ )) }, groupKey))
329
+ ] }),
330
+ /* @__PURE__ */ jsxs("div", { className: "border-t border-[var(--md3-outline-variant)] px-3 py-1.5 text-[10px] text-muted-foreground", children: [
331
+ filtered.length,
332
+ " of ",
333
+ deduped.length,
334
+ " model",
335
+ deduped.length === 1 ? "" : "s"
336
+ ] })
337
+ ]
338
+ }
339
+ ) })
340
+ ] })
341
+ ] });
342
+ }
343
+ function Section({ label, children, tone }) {
344
+ const identity = brandInfo(normalizeBrandKey(label));
345
+ return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
346
+ /* @__PURE__ */ jsxs(
347
+ "div",
348
+ {
349
+ className: cn(
350
+ "flex items-center gap-1.5 px-3 pt-1.5 pb-0.5 text-[10px] font-mono uppercase tracking-widest",
351
+ tone === "featured" ? "text-primary" : "text-muted-foreground"
352
+ ),
353
+ children: [
354
+ identity.key !== "unknown" && /* @__PURE__ */ jsx(BrandLogo, { brand: identity, size: "xs" }),
355
+ /* @__PURE__ */ jsx("span", { children: label })
356
+ ]
357
+ }
358
+ ),
359
+ /* @__PURE__ */ jsx("div", { children })
360
+ ] });
361
+ }
362
+ function PickerItem({
363
+ onSelect,
364
+ active,
365
+ featured,
366
+ children
367
+ }) {
368
+ return /* @__PURE__ */ jsx(
369
+ Popover.Item,
370
+ {
371
+ onSelect: (e) => {
372
+ e.preventDefault();
373
+ onSelect();
374
+ },
375
+ className: cn(
376
+ "flex cursor-pointer items-start gap-2 rounded-md px-3 py-2 outline-none",
377
+ "transition-colors duration-[var(--transition-fast)]",
378
+ "hover:bg-accent/50 focus:bg-accent/50",
379
+ featured && "mx-1 border border-primary/10 bg-primary/[0.04] px-2.5",
380
+ active && "bg-primary/10 font-medium text-foreground ring-1 ring-inset ring-primary/25 hover:bg-primary/15 focus:bg-primary/15"
381
+ ),
382
+ children
383
+ }
384
+ );
385
+ }
386
+ function ModelRow({
387
+ model,
388
+ active,
389
+ onSelect,
390
+ featured
391
+ }) {
392
+ const id = canonicalModelId(model);
393
+ const pricing = formatPricing(model.pricing);
394
+ const ctx = formatContext(model.context_length);
395
+ const identity = resolveModelBrandIdentity(model);
396
+ const via = !identity.combined ? identity.host.label : null;
397
+ return /* @__PURE__ */ jsxs(PickerItem, { onSelect: () => onSelect(id), active, featured, children: [
398
+ /* @__PURE__ */ jsx(ModelBrandStack, { identity, size: "md" }),
399
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
400
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
401
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-sm font-medium", children: model.name ?? model.id }),
402
+ via && /* @__PURE__ */ jsxs("span", { className: "shrink-0 rounded-full border border-[var(--md3-outline-variant)] bg-surface-container px-1.5 py-px text-[9px] font-medium text-muted-foreground", children: [
403
+ "via ",
404
+ via
405
+ ] })
406
+ ] }),
407
+ (ctx || pricing) && /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
408
+ ctx && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: ctx }),
409
+ ctx && pricing && /* @__PURE__ */ jsx("span", { className: "shrink-0 opacity-40", children: "\xB7" }),
410
+ pricing && /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono", children: pricing })
411
+ ] })
412
+ ] })
413
+ ] });
414
+ }
415
+
416
+ export {
417
+ modelDedupKey,
418
+ dedupeModels,
419
+ DEFAULT_FEATURED_MODEL_IDS,
420
+ formatPricing,
421
+ formatContext,
422
+ stripBrandPrefix,
423
+ ModelPicker
424
+ };
@@ -5,14 +5,16 @@ import {
5
5
  HARNESS_OPTIONS,
6
6
  HarnessLogo,
7
7
  chatCapableHarnesses
8
- } from "./chunk-IWIZ7RZE.js";
8
+ } from "./chunk-2T6KBMLN.js";
9
9
  import {
10
- ModelBrandStack,
11
10
  ModelPicker,
12
- canonicalModelId,
13
- resolveModelBrandIdentity,
14
11
  stripBrandPrefix
15
- } from "./chunk-L62ZT6RS.js";
12
+ } from "./chunk-R4AR3TKR.js";
13
+ import {
14
+ ModelBrandStack,
15
+ canonicalModelId,
16
+ resolveModelBrandIdentity
17
+ } from "./chunk-I3G23XHN.js";
16
18
  import {
17
19
  cn
18
20
  } from "./chunk-EI44GEQ5.js";
@@ -3,10 +3,11 @@ import * as React from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { HarnessType } from '@tangle-network/agent-interface';
5
5
  export { HarnessType } from '@tangle-network/agent-interface';
6
- export { D as DEFAULT_FEATURED_MODEL_IDS, M as ModelInfo, a as ModelPicker, b as ModelPickerProps, c as ModelPickerVariant, d as canonicalModelId, e as dedupeModels, f as formatContext, g as formatPricing, m as modelDedupKey } from './model-picker-COjPVMzg.js';
6
+ import * as Popover from '@radix-ui/react-dropdown-menu';
7
+ import { b as ModelInfo } from './model-brand-CsFykzMA.js';
8
+ export { M as ModelBrandIdentity, a as ModelBrandStack, c as canonicalModelId, m as modelBrandFor } from './model-brand-CsFykzMA.js';
7
9
  export { B as BillingBalance, a as BillingDashboard, b as BillingDashboardProps, c as BillingSubscription, d as BillingUsage, P as PricingPage, e as PricingPageProps, f as PricingTier, T as TemplateCard, g as TemplateCardData, h as TemplateCardProps, U as UsageChart, i as UsageChartProps, j as UsageDataPoint, k as formatPrice } from './template-card-UhV3pmRC.js';
8
10
  export { E as ExposedPort, P as PortsList, a as PortsListProps } from './ports-list-DfG0VfPt.js';
9
- import '@radix-ui/react-dropdown-menu';
10
11
 
11
12
  interface SidebarUser {
12
13
  email: string;
@@ -781,6 +782,82 @@ interface HarnessLogoProps {
781
782
  */
782
783
  declare function HarnessLogo({ type, size, className }: HarnessLogoProps): react_jsx_runtime.JSX.Element;
783
784
 
785
+ type ModelPickerVariant = "field" | "pill";
786
+ interface ModelPickerProps {
787
+ /** Canonical model id (provider-prefixed, e.g. "openai/gpt-5.4"). */
788
+ value: string;
789
+ onChange: (modelId: string) => void;
790
+ /** Models to choose from. Pass `[]` while loading. */
791
+ models: ModelInfo[];
792
+ /** Show the loading state (overrides empty-list copy). */
793
+ loading?: boolean;
794
+ /** Recently-used canonical ids to surface at the top. */
795
+ recents?: ReadonlyArray<string>;
796
+ /**
797
+ * Canonical model ids to surface in a "Popular" section above the full
798
+ * list. Each id is resolved against `models`; ids not present in the
799
+ * loaded catalog are silently skipped, so callers can pass a stable
800
+ * curation list without worrying about provider availability.
801
+ */
802
+ popular?: ReadonlyArray<string>;
803
+ /** Drop providers from the picker entirely (e.g. "audio", "embedding"). */
804
+ excludeProviders?: ReadonlyArray<string>;
805
+ /** Restrict to these architectures (e.g. ["text"]). Default: all. */
806
+ modalities?: ReadonlyArray<string>;
807
+ /** Trigger appearance. "field" = full-width form field; "pill" = inline chat input pill. */
808
+ variant?: ModelPickerVariant;
809
+ label?: string;
810
+ placeholder?: string;
811
+ className?: string;
812
+ /**
813
+ * Extra classes merged onto the trigger button itself (after the
814
+ * built-in defaults, so callers can override sizing/radius/font to
815
+ * match a surrounding form). Distinct from `className`, which on the
816
+ * "field" variant styles the outer wrapper.
817
+ */
818
+ triggerClassName?: string;
819
+ disabled?: boolean;
820
+ /** Side the menu opens toward. Defaults to Radix's "bottom". */
821
+ side?: Popover.DropdownMenuContentProps["side"];
822
+ /**
823
+ * Let Radix flip the menu to the opposite side when it would overflow the
824
+ * viewport. Defaults to `true` (Radix default). Pass `false` to pin the menu
825
+ * to `side` — e.g. a composer floating in open space that should always open
826
+ * downward rather than flipping up over the heading.
827
+ */
828
+ avoidCollisions?: boolean;
829
+ }
830
+ /**
831
+ * Stable key used to collapse catalog duplicates. The Tangle Router lists the
832
+ * same underlying model under several host prefixes (e.g. `openai/gpt-5.4`,
833
+ * `tcloud/gpt-5.4`, `openrouter/openai/gpt-5.4`); they all name one model and
834
+ * should occupy a single row. The key is `<lab>/<model-id>`: the authoring lab
835
+ * (inferred via {@link resolveModelBrandIdentity}, which already maps
836
+ * `gpt-5.4` → openai, `kimi-k2` → moonshot, etc.) plus the final id segment,
837
+ * so a model collapses to one identity regardless of which host serves it.
838
+ */
839
+ declare function modelDedupKey(model: ModelInfo): string;
840
+ /**
841
+ * Collapse catalog duplicates to one row per {@link modelDedupKey}. When a
842
+ * model is served under multiple hosts the preferred row wins: a `featured`
843
+ * row beats a non-featured one, otherwise the first occurrence is kept (so
844
+ * caller-controlled catalog order still decides ties). Input order is
845
+ * preserved for the surviving rows.
846
+ */
847
+ declare function dedupeModels(models: ReadonlyArray<ModelInfo>): ModelInfo[];
848
+ /**
849
+ * Fallback "Recommended" seed — latest frontier models per major lab, matched
850
+ * by {@link modelDedupKey} against the loaded catalog. Used only when no
851
+ * catalog row carries an explicit `featured` flag, so a standard router
852
+ * catalog still gets a curated top section without per-deployment config.
853
+ */
854
+ declare const DEFAULT_FEATURED_MODEL_IDS: ReadonlyArray<string>;
855
+ /** Format $/M tokens. Returns null if pricing is missing or zero. */
856
+ declare function formatPricing(pricing: ModelInfo["pricing"]): string | null;
857
+ /** Format context length compactly (e.g. 200_000 → "200k"). */
858
+ declare function formatContext(ctx: number | undefined): string | null;
859
+ declare function ModelPicker({ value, onChange, models, loading, recents, popular, excludeProviders, modalities, variant, label, placeholder, className, triggerClassName, disabled, side, avoidCollisions, }: ModelPickerProps): react_jsx_runtime.JSX.Element;
860
+
784
861
  type ProductVariant = "sandbox";
785
862
  interface NavItem {
786
863
  id: string;
@@ -1269,4 +1346,4 @@ interface InfoPanelProps {
1269
1346
  }
1270
1347
  declare function InfoPanel({ label, title, description, className }: InfoPanelProps): react_jsx_runtime.JSX.Element;
1271
1348
 
1272
- export { ActivityFeed, type ActivityFeedProps, type ActivityItem, type AppearanceController, type Backend, BackendConfig, type BackendConfigProps, BackendSelector, type BackendSelectorProps, type BackendStatusData, ClusterStatusBar, type ClusterStatusBarProps, type ClusterStatusItem, CreditBalance, type CreditBalanceProps, DashboardLayout, type DashboardLayoutProps, type Profile as DashboardProfile, type SnapshotInfo as DashboardSnapshotInfo, type DashboardUser, type GitCommitData, GitPanel, type GitPanelProps, type GitStatusData, HARNESS_BRAND, HARNESS_OPTIONS, type HarnessBrand, HarnessLogo, type HarnessLogoProps, HarnessPicker, type HarnessPickerProps, INSUFFICIENT_BALANCE_CODE, InfoPanel, type InfoPanelProps, type InsufficientBalance, type Invoice, InvoiceTable, type InvoiceTableProps, type McpServer, MetricAreaChart, type MetricAreaChartProps, type MetricChartPoint, type MetricChartTone, type NavItem, NetworkConfig, type NetworkConfigData, type NetworkConfigProps, NewSandboxCard, type NewSandboxCardProps, OutOfCreditsModal, type OutOfCreditsModalProps, type PlanCardData, PlanCards, type PlanCardsProps, type PlanFeature, type ProcessInfo, ProcessList, type ProcessListProps, type ProductVariant, ProfileAvatar, type ProfileAvatarProps, ProfileComparison, type ProfileComparisonProps, ProfileSelector, type ProfileSelectorProps, PromoBanner, type PromoBannerProps, RailButton, type RailButtonProps, RailCollapseToggle, type RailCollapseToggleProps, RailExpandable, type RailExpandableProps, type RailExpandableSubItem, RailFlyout, type RailFlyoutProps, RailHeader, type RailHeaderProps, RailModeButton, type RailModeButtonProps, RailSeparator, type RailSeparatorProps, RailThemeToggle, RailTooltip, type RailTooltipProps, ResourceMeter, type ResourceMeterProps, ResourceSnapshot, type ResourceSnapshotItem, type ResourceSnapshotProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_LABELED_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, type SandboxCardData, type SandboxCardProps, type SandboxStatus, SandboxTable, type SandboxTableProps, Sidebar, SidebarContent, type SidebarContentProps, SidebarLayout, type SidebarLayoutFlyoutItem, type SidebarLayoutNavItem, type SidebarLayoutProps, SidebarPanel, SidebarPanelContent, type SidebarPanelContentProps, SidebarPanelHeader, type SidebarPanelHeaderProps, type SidebarPanelProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarRail, SidebarRailFooter, type SidebarRailFooterProps, SidebarRailHeader, type SidebarRailHeaderProps, SidebarRailNav, type SidebarRailNavProps, type SidebarRailProps, type SidebarUser, SnapshotList, type SnapshotListProps, SystemLogsViewer, type SystemLogsViewerProps, type TeamRole, type ThemeMode, UsageSummary, type UsageSummaryData, type UsageSummaryProps, type Variant, VariantList, type VariantListProps, type VariantOutcome, type VariantStatus, canAdminSandbox, chatCapableHarnesses, parseInsufficientBalance, useSidebar };
1349
+ export { ActivityFeed, type ActivityFeedProps, type ActivityItem, type AppearanceController, type Backend, BackendConfig, type BackendConfigProps, BackendSelector, type BackendSelectorProps, type BackendStatusData, ClusterStatusBar, type ClusterStatusBarProps, type ClusterStatusItem, CreditBalance, type CreditBalanceProps, DEFAULT_FEATURED_MODEL_IDS, DashboardLayout, type DashboardLayoutProps, type Profile as DashboardProfile, type SnapshotInfo as DashboardSnapshotInfo, type DashboardUser, type GitCommitData, GitPanel, type GitPanelProps, type GitStatusData, HARNESS_BRAND, HARNESS_OPTIONS, type HarnessBrand, HarnessLogo, type HarnessLogoProps, HarnessPicker, type HarnessPickerProps, INSUFFICIENT_BALANCE_CODE, InfoPanel, type InfoPanelProps, type InsufficientBalance, type Invoice, InvoiceTable, type InvoiceTableProps, type McpServer, MetricAreaChart, type MetricAreaChartProps, type MetricChartPoint, type MetricChartTone, ModelInfo, ModelPicker, type ModelPickerProps, type ModelPickerVariant, type NavItem, NetworkConfig, type NetworkConfigData, type NetworkConfigProps, NewSandboxCard, type NewSandboxCardProps, OutOfCreditsModal, type OutOfCreditsModalProps, type PlanCardData, PlanCards, type PlanCardsProps, type PlanFeature, type ProcessInfo, ProcessList, type ProcessListProps, type ProductVariant, ProfileAvatar, type ProfileAvatarProps, ProfileComparison, type ProfileComparisonProps, ProfileSelector, type ProfileSelectorProps, PromoBanner, type PromoBannerProps, RailButton, type RailButtonProps, RailCollapseToggle, type RailCollapseToggleProps, RailExpandable, type RailExpandableProps, type RailExpandableSubItem, RailFlyout, type RailFlyoutProps, RailHeader, type RailHeaderProps, RailModeButton, type RailModeButtonProps, RailSeparator, type RailSeparatorProps, RailThemeToggle, RailTooltip, type RailTooltipProps, ResourceMeter, type ResourceMeterProps, ResourceSnapshot, type ResourceSnapshotItem, type ResourceSnapshotProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_LABELED_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, type SandboxCardData, type SandboxCardProps, type SandboxStatus, SandboxTable, type SandboxTableProps, Sidebar, SidebarContent, type SidebarContentProps, SidebarLayout, type SidebarLayoutFlyoutItem, type SidebarLayoutNavItem, type SidebarLayoutProps, SidebarPanel, SidebarPanelContent, type SidebarPanelContentProps, SidebarPanelHeader, type SidebarPanelHeaderProps, type SidebarPanelProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarRail, SidebarRailFooter, type SidebarRailFooterProps, SidebarRailHeader, type SidebarRailHeaderProps, SidebarRailNav, type SidebarRailNavProps, type SidebarRailProps, type SidebarUser, SnapshotList, type SnapshotListProps, SystemLogsViewer, type SystemLogsViewerProps, type TeamRole, type ThemeMode, UsageSummary, type UsageSummaryData, type UsageSummaryProps, type Variant, VariantList, type VariantListProps, type VariantOutcome, type VariantStatus, canAdminSandbox, chatCapableHarnesses, dedupeModels, formatContext, formatPricing, modelDedupKey, parseInsufficientBalance, useSidebar };
package/dist/dashboard.js CHANGED
@@ -71,16 +71,20 @@ import {
71
71
  HarnessLogo,
72
72
  HarnessPicker,
73
73
  chatCapableHarnesses
74
- } from "./chunk-IWIZ7RZE.js";
74
+ } from "./chunk-2T6KBMLN.js";
75
75
  import {
76
76
  DEFAULT_FEATURED_MODEL_IDS,
77
77
  ModelPicker,
78
- canonicalModelId,
79
78
  dedupeModels,
80
79
  formatContext,
81
80
  formatPricing,
82
81
  modelDedupKey
83
- } from "./chunk-L62ZT6RS.js";
82
+ } from "./chunk-R4AR3TKR.js";
83
+ import {
84
+ ModelBrandStack,
85
+ canonicalModelId,
86
+ modelBrandFor
87
+ } from "./chunk-I3G23XHN.js";
84
88
  import "./chunk-EI44GEQ5.js";
85
89
  import "./chunk-U67V476Y.js";
86
90
  export {
@@ -101,6 +105,7 @@ export {
101
105
  InfoPanel,
102
106
  InvoiceTable,
103
107
  MetricAreaChart,
108
+ ModelBrandStack,
104
109
  ModelPicker,
105
110
  NetworkConfig,
106
111
  NewSandboxCard,
@@ -155,6 +160,7 @@ export {
155
160
  formatContext,
156
161
  formatPrice,
157
162
  formatPricing,
163
+ modelBrandFor,
158
164
  modelDedupKey,
159
165
  parseInsufficientBalance,
160
166
  useSidebar
package/dist/globals.css CHANGED
@@ -444,6 +444,9 @@
444
444
  .left-full {
445
445
  left: 100%;
446
446
  }
447
+ .isolate {
448
+ isolation: isolate;
449
+ }
447
450
  .z-0 {
448
451
  z-index: 0;
449
452
  }
package/dist/index.d.ts CHANGED
@@ -9,8 +9,7 @@ export { AgentComposer, AgentComposerProps, AgentProfileCapability, AgentProfile
9
9
  export { ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from '@tangle-network/ui/run';
10
10
  export { CommandPreview, DiffPreview, GlobResultsPreview, GrepResultsPreview, QuestionPreview, WebSearchPreview, WriteFilePreview } from '@tangle-network/ui/tool-previews';
11
11
  export { FileArtifactPane, FileArtifactPaneProps, FileNode, FilePreview, FilePreviewProps, FileTabData, FileTabs, FileTabsProps, FileTree, FileTreeProps, FileTreeVisibilityOptions, RichFileTree, RichFileTreeGitEntry, RichFileTreeGitStatus, RichFileTreeProps, RichFileTreeThemeVars, filterFileTree } from '@tangle-network/ui/files';
12
- export { ActivityFeed, ActivityFeedProps, ActivityItem, AppearanceController, Backend, BackendConfig, BackendConfigProps, BackendSelector, BackendSelectorProps, BackendStatusData, ClusterStatusBar, ClusterStatusBarProps, ClusterStatusItem, CreditBalance, CreditBalanceProps, DashboardLayout, DashboardLayoutProps, DashboardProfile, DashboardSnapshotInfo, DashboardUser, GitCommitData, GitPanel, GitPanelProps, GitStatusData, HARNESS_BRAND, HARNESS_OPTIONS, HarnessBrand, HarnessLogo, HarnessLogoProps, HarnessPicker, HarnessPickerProps, INSUFFICIENT_BALANCE_CODE, InfoPanel, InfoPanelProps, InsufficientBalance, Invoice, InvoiceTable, InvoiceTableProps, McpServer, MetricAreaChart, MetricAreaChartProps, MetricChartPoint, MetricChartTone, NavItem, NetworkConfig, NetworkConfigData, NetworkConfigProps, NewSandboxCard, NewSandboxCardProps, OutOfCreditsModal, OutOfCreditsModalProps, PlanCardData, PlanCards, PlanCardsProps, PlanFeature, ProcessInfo, ProcessList, ProcessListProps, ProductVariant, ProfileAvatar, ProfileAvatarProps, ProfileComparison, ProfileComparisonProps, ProfileSelector, ProfileSelectorProps, PromoBanner, PromoBannerProps, RailButton, RailButtonProps, RailCollapseToggle, RailCollapseToggleProps, RailExpandable, RailExpandableProps, RailExpandableSubItem, RailFlyout, RailFlyoutProps, RailHeader, RailHeaderProps, RailModeButton, RailModeButtonProps, RailSeparator, RailSeparatorProps, RailThemeToggle, RailTooltip, RailTooltipProps, ResourceMeter, ResourceMeterProps, ResourceSnapshot, ResourceSnapshotItem, ResourceSnapshotProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_LABELED_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, SandboxCardData, SandboxCardProps, SandboxStatus, SandboxTable, SandboxTableProps, Sidebar, SidebarContent, SidebarContentProps, SidebarLayout, SidebarLayoutFlyoutItem, SidebarLayoutNavItem, SidebarLayoutProps, SidebarPanel, SidebarPanelContent, SidebarPanelContentProps, SidebarPanelHeader, SidebarPanelHeaderProps, SidebarPanelProps, SidebarProps, SidebarProvider, SidebarProviderProps, SidebarRail, SidebarRailFooter, SidebarRailFooterProps, SidebarRailHeader, SidebarRailHeaderProps, SidebarRailNav, SidebarRailNavProps, SidebarRailProps, SidebarUser, SnapshotList, SnapshotListProps, SystemLogsViewer, SystemLogsViewerProps, TeamRole, ThemeMode, UsageSummary, UsageSummaryData, UsageSummaryProps, Variant, VariantList, VariantListProps, VariantOutcome, VariantStatus, canAdminSandbox, chatCapableHarnesses, parseInsufficientBalance, useSidebar } from './dashboard.js';
13
- export { D as DEFAULT_FEATURED_MODEL_IDS, M as ModelInfo, a as ModelPicker, b as ModelPickerProps, c as ModelPickerVariant, d as canonicalModelId, e as dedupeModels, f as formatContext, g as formatPricing, m as modelDedupKey } from './model-picker-COjPVMzg.js';
12
+ export { ActivityFeed, ActivityFeedProps, ActivityItem, AppearanceController, Backend, BackendConfig, BackendConfigProps, BackendSelector, BackendSelectorProps, BackendStatusData, ClusterStatusBar, ClusterStatusBarProps, ClusterStatusItem, CreditBalance, CreditBalanceProps, DEFAULT_FEATURED_MODEL_IDS, DashboardLayout, DashboardLayoutProps, DashboardProfile, DashboardSnapshotInfo, DashboardUser, GitCommitData, GitPanel, GitPanelProps, GitStatusData, HARNESS_BRAND, HARNESS_OPTIONS, HarnessBrand, HarnessLogo, HarnessLogoProps, HarnessPicker, HarnessPickerProps, INSUFFICIENT_BALANCE_CODE, InfoPanel, InfoPanelProps, InsufficientBalance, Invoice, InvoiceTable, InvoiceTableProps, McpServer, MetricAreaChart, MetricAreaChartProps, MetricChartPoint, MetricChartTone, ModelPicker, ModelPickerProps, ModelPickerVariant, NavItem, NetworkConfig, NetworkConfigData, NetworkConfigProps, NewSandboxCard, NewSandboxCardProps, OutOfCreditsModal, OutOfCreditsModalProps, PlanCardData, PlanCards, PlanCardsProps, PlanFeature, ProcessInfo, ProcessList, ProcessListProps, ProductVariant, ProfileAvatar, ProfileAvatarProps, ProfileComparison, ProfileComparisonProps, ProfileSelector, ProfileSelectorProps, PromoBanner, PromoBannerProps, RailButton, RailButtonProps, RailCollapseToggle, RailCollapseToggleProps, RailExpandable, RailExpandableProps, RailExpandableSubItem, RailFlyout, RailFlyoutProps, RailHeader, RailHeaderProps, RailModeButton, RailModeButtonProps, RailSeparator, RailSeparatorProps, RailThemeToggle, RailTooltip, RailTooltipProps, ResourceMeter, ResourceMeterProps, ResourceSnapshot, ResourceSnapshotItem, ResourceSnapshotProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_LABELED_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, SandboxCardData, SandboxCardProps, SandboxStatus, SandboxTable, SandboxTableProps, Sidebar, SidebarContent, SidebarContentProps, SidebarLayout, SidebarLayoutFlyoutItem, SidebarLayoutNavItem, SidebarLayoutProps, SidebarPanel, SidebarPanelContent, SidebarPanelContentProps, SidebarPanelHeader, SidebarPanelHeaderProps, SidebarPanelProps, SidebarProps, SidebarProvider, SidebarProviderProps, SidebarRail, SidebarRailFooter, SidebarRailFooterProps, SidebarRailHeader, SidebarRailHeaderProps, SidebarRailNav, SidebarRailNavProps, SidebarRailProps, SidebarUser, SnapshotList, SnapshotListProps, SystemLogsViewer, SystemLogsViewerProps, TeamRole, ThemeMode, UsageSummary, UsageSummaryData, UsageSummaryProps, Variant, VariantList, VariantListProps, VariantOutcome, VariantStatus, canAdminSandbox, chatCapableHarnesses, dedupeModels, formatContext, formatPricing, modelDedupKey, parseInsufficientBalance, useSidebar } from './dashboard.js';
14
13
  export { B as BillingBalance, a as BillingDashboard, b as BillingDashboardProps, c as BillingSubscription, d as BillingUsage, P as PricingPage, e as PricingPageProps, f as PricingTier, T as TemplateCard, g as TemplateCardData, h as TemplateCardProps, U as UsageChart, i as UsageChartProps, j as UsageDataPoint, k as formatPrice } from './template-card-UhV3pmRC.js';
15
14
  export { E as ExposedPort, P as PortsList, a as PortsListProps } from './ports-list-DfG0VfPt.js';
16
15
  export { AuthHeader, AuthHeaderProps, GitHubLoginButton, GitHubLoginButtonProps, LoginLayout, LoginLayoutProps, SessionUser, UserMenu, UserMenuProps } from '@tangle-network/ui/auth';
@@ -24,6 +23,7 @@ export { AgentBranding, CustomToolRenderer, DisplayVariant, FinalTextPart, Group
24
23
  export { Session } from './types.js';
25
24
  export { TOOL_CATEGORY_ICONS, cn, copyText, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, timeAgo, truncateText } from '@tangle-network/ui/utils';
26
25
  export { HarnessType, modelProvider, snapHarnessToModel } from '@tangle-network/agent-interface';
26
+ export { M as ModelBrandIdentity, a as ModelBrandStack, b as ModelInfo, c as canonicalModelId, m as modelBrandFor } from './model-brand-CsFykzMA.js';
27
27
  import 'react/jsx-runtime';
28
28
  import 'react';
29
29
  import '@radix-ui/react-dropdown-menu';