From 34aa10a758b36b49182d8a3c9ede971fd2e8cd6e Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Mon, 14 Sep 2026 01:08:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20model=20pill=20=E2=80=94=20brand?= =?UTF-8?q?=20glyph=20+=20model=20name=20capsule=20in=20proxy-logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Model identifiers rendered as plain truncated text; a capsule with the brand glyph (existing vendored BrandIcon set) reads faster and matches the established models-page brand column. Unknown models degrade to the plain name (no empty pill); token-driven surface works in both themes. Co-authored-by: Cursor --- .../common/__tests__/model-pill.test.tsx | 29 +++++++++++++++++ web/src/components/common/model-pill.tsx | 32 +++++++++++++++++++ .../components/proxy-logs-columns.tsx | 11 +++---- 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 web/src/components/common/__tests__/model-pill.test.tsx create mode 100644 web/src/components/common/model-pill.tsx diff --git a/web/src/components/common/__tests__/model-pill.test.tsx b/web/src/components/common/__tests__/model-pill.test.tsx new file mode 100644 index 00000000..3a6ee5b7 --- /dev/null +++ b/web/src/components/common/__tests__/model-pill.test.tsx @@ -0,0 +1,29 @@ +// Behavior test for ModelPill: known models render the brand glyph inside the +// capsule; unknown models still render the name (never an empty pill); blank +// input renders nothing. + +import '@testing-library/jest-dom/vitest' +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, describe, expect, it } from 'vitest' + +import { ModelPill } from '../model-pill' + +describe('ModelPill', () => { + afterEach(cleanup) + + it('renders the brand glyph for a known model', () => { + const { container } = render() + expect(screen.getByText('gpt-4o-mini')).toBeInTheDocument() + expect(container.querySelector('img')).not.toBeNull() + }) + + it('renders the plain name for an unknown model', () => { + render() + expect(screen.getByText('my-custom-model-v9')).toBeInTheDocument() + }) + + it('renders nothing for a blank model', () => { + const { container } = render() + expect(container).toBeEmptyDOMElement() + }) +}) diff --git a/web/src/components/common/model-pill.tsx b/web/src/components/common/model-pill.tsx new file mode 100644 index 00000000..59afeeba --- /dev/null +++ b/web/src/components/common/model-pill.tsx @@ -0,0 +1,32 @@ +// metapi-go/components/common — model pill: a compact capsule that pairs a +// model name with its brand glyph (BrandIcon) wherever model identifiers +// appear in dense tables (proxy logs first). Token-driven surface only; +// unknown models degrade to the plain truncated name (no empty pill). + +import { InlineBrandIcon } from '@/assets/brand-icons/BrandIcon' +import { cn } from '@/lib/utils' + +export type ModelPillProps = { + /** Resolved model name shown in the pill (usually the actual upstream one). */ + model: string + /** Full label for the tooltip when the cell truncates (e.g. requested→actual). */ + title?: string + className?: string +} + +export function ModelPill({ model, title, className }: ModelPillProps) { + const name = model.trim() + if (!name) return null + return ( + + + {name} + + ) +} diff --git a/web/src/features/proxy-logs/components/proxy-logs-columns.tsx b/web/src/features/proxy-logs/components/proxy-logs-columns.tsx index 21d6309f..9519372d 100644 --- a/web/src/features/proxy-logs/components/proxy-logs-columns.tsx +++ b/web/src/features/proxy-logs/components/proxy-logs-columns.tsx @@ -9,6 +9,7 @@ import { import { useTranslation } from 'react-i18next' import { HttpStatusBadge } from '@/components/common/http-status-badge' +import { ModelPill } from '@/components/common/model-pill' import { DataTableColumnHeader } from '@/components/data-table' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' @@ -136,15 +137,13 @@ export function useProxyLogsColumns( /> ), cell: ({ row }) => ( - - {formatModelCell( + + title={row.original.modelActual || row.original.modelRequested} + /> ), }, {