From 7080c61525cb11bcdef878b799474c55978fc95e Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Wed, 3 Dec 2025 23:22:35 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=8C=20feat:=20Add=20Support=20for=20Pe?= =?UTF-8?q?rsistable=20(Non-Dismissible)=20Banners=20(#10730)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add persistable property to banners and update related components * refactor: Clean up Banner component and improve className handling --- client/src/components/Banners/Banner.tsx | 36 ++++++++++++++-------- config/update-banner.js | 12 +++++++- packages/data-provider/src/schemas.ts | 1 + packages/data-schemas/src/schema/banner.ts | 5 +++ packages/data-schemas/src/types/banner.ts | 1 + 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/client/src/components/Banners/Banner.tsx b/client/src/components/Banners/Banner.tsx index d4bc2e5853..c5c2598849 100644 --- a/client/src/components/Banners/Banner.tsx +++ b/client/src/components/Banners/Banner.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef } from 'react'; import { XIcon } from 'lucide-react'; import { useRecoilState } from 'recoil'; +import { Button, cn } from '@librechat/client'; import { useGetBannerQuery } from '~/data-provider'; import store from '~/store'; @@ -15,34 +16,45 @@ export const Banner = ({ onHeightChange }: { onHeightChange?: (height: number) = } }, [banner, hideBannerHint, onHeightChange]); - if (!banner || (banner.bannerId && hideBannerHint.includes(banner.bannerId))) { + if ( + !banner || + (banner.bannerId && !banner.persistable && hideBannerHint.includes(banner.bannerId)) + ) { return null; } const onClick = () => { + if (banner.persistable) { + return; + } + setHideBannerHint([...hideBannerHint, banner.bannerId]); + if (onHeightChange) { - onHeightChange(0); // Reset height when banner is closed + onHeightChange(0); } }; return (
- + {!banner.persistable && ( + + )}
); }; diff --git a/config/update-banner.js b/config/update-banner.js index 528c33cd67..31d774941b 100644 --- a/config/update-banner.js +++ b/config/update-banner.js @@ -22,15 +22,17 @@ const connect = require('./connect'); let displayTo = ''; let message = ''; let isPublic = undefined; + let persistable = undefined; // If we have the right number of arguments, lets use them if (process.argv.length >= 3) { displayFrom = process.argv[2]; displayTo = process.argv[3]; message = process.argv[4]; isPublic = process.argv[5] === undefined ? undefined : process.argv[5] === 'true'; + persistable = process.argv[6] === undefined ? undefined : process.argv[6] === 'true'; } else { console.orange( - 'Usage: npm run update-banner ', + 'Usage: npm run update-banner ', ); console.orange('Note: if you do not pass in the arguments, you will be prompted for them.'); console.purple('--------------------------'); @@ -81,6 +83,11 @@ const connect = require('./connect'); isPublic = isPublicInput.toLowerCase() === 'y' ? true : false; } + if (persistable === undefined) { + const persistableInput = await askQuestion('Is persistable (cannot be dismissed) (y/N):'); + persistable = persistableInput.toLowerCase() === 'y' ? true : false; + } + // Generate the same bannerId for the same message // This allows us to display only messages that haven't been shown yet const NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // Use an arbitrary namespace UUID @@ -101,6 +108,7 @@ const connect = require('./connect'); message, bannerId, isPublic, + persistable, }, { new: true }, ); @@ -111,6 +119,7 @@ const connect = require('./connect'); message, bannerId, isPublic, + persistable, }); } } catch (error) { @@ -131,6 +140,7 @@ const connect = require('./connect'); console.purple(`to: ${displayTo || 'not specified'}`); console.purple(`Banner: ${message}`); console.purple(`isPublic: ${isPublic}`); + console.purple(`persistable: ${persistable}`); silentExit(0); })(); diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 7c9cb883b7..ff60fc9881 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -1139,6 +1139,7 @@ export const tBannerSchema = z.object({ createdAt: z.string(), updatedAt: z.string(), isPublic: z.boolean(), + persistable: z.boolean().default(false), }); export type TBanner = z.infer; diff --git a/packages/data-schemas/src/schema/banner.ts b/packages/data-schemas/src/schema/banner.ts index 0b4d218344..7cd07a93af 100644 --- a/packages/data-schemas/src/schema/banner.ts +++ b/packages/data-schemas/src/schema/banner.ts @@ -7,6 +7,7 @@ export interface IBanner extends Document { displayTo?: Date; type: 'banner' | 'popup'; isPublic: boolean; + persistable: boolean; } const bannerSchema = new Schema( @@ -36,6 +37,10 @@ const bannerSchema = new Schema( type: Boolean, default: false, }, + persistable: { + type: Boolean, + default: false, + }, }, { timestamps: true }, ); diff --git a/packages/data-schemas/src/types/banner.ts b/packages/data-schemas/src/types/banner.ts index 288ae7e9f8..e9c63ac97e 100644 --- a/packages/data-schemas/src/types/banner.ts +++ b/packages/data-schemas/src/types/banner.ts @@ -7,4 +7,5 @@ export interface IBanner extends Document { displayTo?: Date; type: 'banner' | 'popup'; isPublic: boolean; + persistable: boolean; }