Skip to content

Commit bf736a7

Browse files
authored
Feat(webapp): Models page UI improvements (#3308)
Lots of layout and UI improvements to the new Models page <img width="2282" height="1356" alt="CleanShot 2026-04-01 at 14 14 35" src="https://github.com/user-attachments/assets/4a13a291-5d5b-415f-962f-b9cfb25b887d" />
1 parent f75d4d6 commit bf736a7

File tree

9 files changed

+1032
-412
lines changed

9 files changed

+1032
-412
lines changed

apps/webapp/app/components/primitives/AppliedFilter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function AppliedFilter({
5353
<div
5454
className={cn("flex items-start leading-4", label === undefined ? "gap-1.5" : "gap-0.5")}
5555
>
56-
<div className="-mt-[0.5px] flex items-center gap-1">
56+
<div className="-mt-[0.5px] flex items-center gap-1.5">
5757
{icon}
5858
{label && (
5959
<div className="text-text-bright">

apps/webapp/app/components/primitives/Buttons.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
341341
disabled: isDisabled || !props.shortcut,
342342
});
343343

344-
return (
344+
const buttonElement = (
345345
<button
346346
className={cn("group/button outline-none focus-custom", props.fullWidth ? "w-full" : "")}
347347
type={type}
@@ -353,9 +353,30 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
353353
form={props.form}
354354
autoFocus={autoFocus}
355355
>
356-
<ButtonContent {...props} />
356+
<ButtonContent {...props} tooltip={undefined} />
357357
</button>
358358
);
359+
360+
if (props.tooltip) {
361+
return (
362+
<TooltipProvider>
363+
<Tooltip>
364+
<TooltipTrigger asChild>
365+
<span className={isDisabled ? "cursor-default" : undefined}>
366+
{buttonElement}
367+
</span>
368+
</TooltipTrigger>
369+
<TooltipContent className="flex items-center gap-3 py-1.5 pl-2.5 pr-3 text-xs text-text-bright">
370+
{props.tooltip} {props.shortcut && !props.hideShortcutKey && (
371+
<ShortcutKey shortcut={props.shortcut} variant="medium" className="ml-1" />
372+
)}
373+
</TooltipContent>
374+
</Tooltip>
375+
</TooltipProvider>
376+
);
377+
}
378+
379+
return buttonElement;
359380
}
360381
);
361382

apps/webapp/app/components/primitives/Checkbox.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ export const Checkbox = forwardRef<HTMLInputElement, SimpleCheckboxProps>(
184184
<input
185185
type="checkbox"
186186
className={cn(
187-
props.readOnly || props.disabled ? "cursor-default" : "cursor-pointer",
188-
"read-only:border-charcoal-650 disabled:border-charcoal-650 rounded-sm border border-charcoal-600 bg-transparent transition checked:!bg-indigo-500 read-only:!bg-charcoal-700 group-hover:bg-charcoal-900 group-hover:checked:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-none focus-visible:ring-indigo-500 disabled:!bg-charcoal-700"
187+
props.disabled ? "cursor-not-allowed" : props.readOnly ? "cursor-default" : "cursor-pointer",
188+
"read-only:border-charcoal-650 disabled:border-charcoal-650 disabled:opacity-50 rounded-sm border border-charcoal-600 bg-transparent transition checked:!bg-indigo-500 read-only:!bg-charcoal-700 group-hover:bg-charcoal-900 group-hover:checked:bg-indigo-500 group-focus:ring-1 focus:ring-indigo-500 focus:ring-offset-0 focus:ring-offset-transparent focus-visible:outline-none focus-visible:ring-indigo-500 disabled:!bg-charcoal-700"
189189
)}
190190
{...props}
191191
ref={ref}

apps/webapp/app/presenters/v3/ModelRegistryPresenter.server.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,12 @@ export type ModelCatalogItem = {
6464
description: string | null;
6565
contextWindow: number | null;
6666
maxOutputTokens: number | null;
67-
capabilities: string[];
67+
/** Combined capabilities (from DB) and boolean feature flags (from catalog) as slug strings. */
68+
features: string[];
6869
inputPrice: number | null;
6970
outputPrice: number | null;
7071
/** When the model was publicly released (from startDate on LlmModel). */
7172
releaseDate: string | null;
72-
supportsStructuredOutput: boolean;
73-
supportsParallelToolCalls: boolean;
74-
supportsStreamingToolCalls: boolean;
7573
/** Dated variants of this model (only populated on base models). */
7674
variants: ModelVariant[];
7775
};
@@ -98,6 +96,17 @@ export type ModelDetail = ModelCatalogItem & {
9896
}>;
9997
};
10098

99+
function buildFeatures(
100+
capabilities: string[],
101+
catalogEntry: { supportsStructuredOutput: boolean; supportsParallelToolCalls: boolean; supportsStreamingToolCalls: boolean } | undefined
102+
): string[] {
103+
const features = new Set(capabilities);
104+
if (catalogEntry?.supportsStructuredOutput) features.add("structured_output");
105+
if (catalogEntry?.supportsParallelToolCalls) features.add("parallel_tool_calls");
106+
if (catalogEntry?.supportsStreamingToolCalls) features.add("streaming_tool_calls");
107+
return Array.from(features);
108+
}
109+
101110
export type ModelMetricsPoint = {
102111
minute: string;
103112
callCount: number;
@@ -214,13 +223,10 @@ export class ModelRegistryPresenter extends BasePresenter {
214223
description: m.description,
215224
contextWindow: m.contextWindow,
216225
maxOutputTokens: m.maxOutputTokens,
217-
capabilities: m.capabilities,
226+
features: buildFeatures(m.capabilities, catalogEntry),
218227
inputPrice: inputPrice ? Number(inputPrice.price) : null,
219228
outputPrice: outputPrice ? Number(outputPrice.price) : null,
220229
releaseDate: m.startDate ? m.startDate.toISOString().split("T")[0] : null,
221-
supportsStructuredOutput: catalogEntry?.supportsStructuredOutput ?? false,
222-
supportsParallelToolCalls: catalogEntry?.supportsParallelToolCalls ?? false,
223-
supportsStreamingToolCalls: catalogEntry?.supportsStreamingToolCalls ?? false,
224230
variants: [],
225231
_baseModelName: m.baseModelName,
226232
};
@@ -304,7 +310,7 @@ export class ModelRegistryPresenter extends BasePresenter {
304310

305311
/** Get a single model with full pricing details. */
306312
async getModelDetail(friendlyId: string): Promise<ModelDetail | null> {
307-
const model = await this._replica.llmModel.findUnique({
313+
const model = await this._replica.llmModel.findFirst({
308314
where: { friendlyId },
309315
include: {
310316
pricingTiers: {
@@ -331,13 +337,10 @@ export class ModelRegistryPresenter extends BasePresenter {
331337
description: model.description,
332338
contextWindow: model.contextWindow,
333339
maxOutputTokens: model.maxOutputTokens,
334-
capabilities: model.capabilities,
340+
features: buildFeatures(model.capabilities, catalogEntry),
335341
inputPrice: inputPrice ? Number(inputPrice.price) : null,
336342
outputPrice: outputPrice ? Number(outputPrice.price) : null,
337343
releaseDate: model.startDate ? model.startDate.toISOString().split("T")[0] : null,
338-
supportsStructuredOutput: catalogEntry?.supportsStructuredOutput ?? false,
339-
supportsParallelToolCalls: catalogEntry?.supportsParallelToolCalls ?? false,
340-
supportsStreamingToolCalls: catalogEntry?.supportsStreamingToolCalls ?? false,
341344
variants: [],
342345
matchPattern: model.matchPattern,
343346
source: model.source,

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.models.$modelId/route.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
formatModelPrice,
4343
formatTokenCount,
4444
formatModelCost,
45-
formatCapability,
45+
formatFeature,
4646
formatProviderName,
4747
} from "~/utils/modelFormatters";
4848

@@ -312,14 +312,14 @@ function OverviewTab({
312312
</Property.Value>
313313
</Property.Item>
314314
)}
315-
{model.capabilities.length > 0 && (
315+
{model.features.length > 0 && (
316316
<Property.Item>
317-
<Property.Label>Capabilities</Property.Label>
317+
<Property.Label>Features</Property.Label>
318318
<Property.Value>
319319
<div className="flex flex-wrap gap-1">
320-
{model.capabilities.map((cap) => (
321-
<Badge key={cap} variant="outline-rounded">
322-
{formatCapability(cap)}
320+
{model.features.map((f) => (
321+
<Badge key={f} variant="outline-rounded">
322+
{formatFeature(f)}
323323
</Badge>
324324
))}
325325
</div>

0 commit comments

Comments
 (0)