From 470be2395fb8a24c8e66512dbcefe105b3311b82 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 10 Jun 2026 21:02:22 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Surface=20Model=20Spec=20Br?= =?UTF-8?q?anding=20on=20Landing=20and=20Selector=20(#13662)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an opt-in showOnLanding flag to model specs. When set, the chat landing shows the spec's label and description in place of the time-of-day greeting; specs without the flag are unaffected, so existing deployments see no behavior change. HTML-valued descriptions (inline icons + markup) render sanitized via the shared config-HTML sanitizer with a new media tag/attribute allowlist, both on the landing and in model selector items. Excludes e2e specs from the typed client lint block so staged e2e files no longer fail pre-commit with 'file not found in project'. --- client/src/components/Chat/Landing.tsx | 47 +++++++++++++++---- .../Endpoints/components/ModelSpecItem.tsx | 5 +- .../Endpoints/components/SearchResults.tsx | 5 +- .../Endpoints/components/SpecDescription.tsx | 32 +++++++++++++ .../__tests__/SpecDescription.spec.tsx | 39 +++++++++++++++ client/src/utils/__tests__/configHtml.test.ts | 16 +++++++ client/src/utils/configHtml.ts | 2 + e2e/config/librechat.e2e.yaml | 8 ++++ e2e/specs/mock/model-spec-branding.spec.ts | 47 +++++++++++++++++++ eslint.config.mjs | 5 +- packages/data-provider/src/models.ts | 3 ++ 11 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 client/src/components/Chat/Menus/Endpoints/components/SpecDescription.tsx create mode 100644 client/src/components/Chat/Menus/Endpoints/components/__tests__/SpecDescription.spec.tsx create mode 100644 e2e/specs/mock/model-spec-branding.spec.ts diff --git a/client/src/components/Chat/Landing.tsx b/client/src/components/Chat/Landing.tsx index 7707fe7066..d3a3d3c8b1 100644 --- a/client/src/components/Chat/Landing.tsx +++ b/client/src/components/Chat/Landing.tsx @@ -2,11 +2,18 @@ import { useMemo, useCallback, useState, useEffect, useRef } from 'react'; import { easings } from '@react-spring/web'; import { EModelEndpoint } from 'librechat-data-provider'; import { BirthdayIcon, TooltipAnchor, SplitText } from '@librechat/client'; +import { + getIconEndpoint, + getEntity, + getModelSpec, + createConfigHtmlSanitizer, + CONFIG_HTML_MEDIA_TAGS, + CONFIG_HTML_MEDIA_ATTR, +} from '~/utils'; import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers'; import { useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider'; import ConvoIcon from '~/components/Endpoints/ConvoIcon'; import { useLocalize, useAuthContext } from '~/hooks'; -import { getIconEndpoint, getEntity } from '~/utils'; const containerClassName = 'shadow-stroke relative flex h-full items-center justify-center rounded-full bg-white dark:bg-presentation dark:text-white text-black dark:after:shadow-none '; @@ -61,8 +68,26 @@ export default function Landing({ centerFormOnLanding }: { centerFormOnLanding: assistant_id: conversation?.assistant_id, }); - const name = entity?.name ?? ''; - const description = (entity?.description || conversation?.greeting) ?? ''; + const modelSpec = useMemo( + () => getModelSpec({ specName: conversation?.spec, startupConfig }), + [conversation?.spec, startupConfig], + ); + + const brandedSpecLabel = modelSpec?.showOnLanding ? modelSpec.label : ''; + const brandedSpecDescription = (modelSpec?.showOnLanding && modelSpec.description) || ''; + const name = entity?.name ?? brandedSpecLabel; + const description = + (entity?.description || brandedSpecDescription || conversation?.greeting) ?? ''; + const descriptionIsHTML = description.trim().startsWith('<'); + + const sanitizeDescription = useMemo( + () => + createConfigHtmlSanitizer({ + allowedTags: CONFIG_HTML_MEDIA_TAGS, + allowedAttr: CONFIG_HTML_MEDIA_ATTR, + }), + [], + ); const getGreeting = useCallback(() => { if (typeof startupConfig?.interface?.customWelcome === 'string') { @@ -198,11 +223,17 @@ export default function Landing({ centerFormOnLanding }: { centerFormOnLanding: /> )} - {description && ( -
- {description} -
- )} + {description && + (descriptionIsHTML ? ( +
+ ) : ( +
+ {description} +
+ ))}
); diff --git a/client/src/components/Chat/Menus/Endpoints/components/ModelSpecItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/ModelSpecItem.tsx index a73d1d8e47..10662fa55b 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/ModelSpecItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/ModelSpecItem.tsx @@ -5,6 +5,7 @@ import type { TModelSpec } from 'librechat-data-provider'; import { useFavorites, useLocalize, useIsActiveItem } from '~/hooks'; import { useModelSelectorContext } from '../ModelSelectorContext'; import { CustomMenuItem as MenuItem } from '../CustomMenu'; +import SpecDescription from './SpecDescription'; import SpecIcon from './SpecIcon'; import { cn } from '~/utils'; @@ -48,9 +49,7 @@ export function ModelSpecItem({ spec, isSelected }: ModelSpecItemProps) { )}
{spec.label} - {spec.description && ( - {spec.description} - )} +