mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
fix(client): address theme env, dialog padding and locked button review feedback
Expose every IThemeRGB token through REACT_APP_THEME_* instead of the hand-maintained subset that omitted the status, destructive, inverted and fixed families. Drop the padding OGDialogContent contributes to the Tool Library so the header divider spans the panel again, and stop disabled:opacity-100 from overriding the locked delete-account button's dimmed state.
This commit is contained in:
parent
c1c3d67837
commit
25b949bbfa
5 changed files with 126 additions and 62 deletions
|
|
@ -183,10 +183,10 @@ const renderDeleteButton = (
|
|||
<Button
|
||||
variant="destructive"
|
||||
className={cn(
|
||||
'mt-4 w-full gap-0 bg-surface-tertiary text-text-primary transition-all duration-200 hover:bg-surface-tertiary disabled:opacity-100',
|
||||
'mt-4 w-full gap-0 bg-surface-tertiary text-text-primary transition-all duration-200 hover:bg-surface-tertiary',
|
||||
isLocked
|
||||
? 'cursor-not-allowed opacity-30'
|
||||
: 'bg-surface-destructive text-white hover:bg-surface-destructive-hover',
|
||||
? 'cursor-not-allowed opacity-30 disabled:opacity-30'
|
||||
: 'bg-surface-destructive text-white hover:bg-surface-destructive-hover disabled:opacity-100',
|
||||
)}
|
||||
onClick={handleDeleteUser}
|
||||
disabled={isDeleting || isLocked}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ function AssistantToolsDialog({
|
|||
}}
|
||||
>
|
||||
<OGDialogContent
|
||||
className="overflow-hidden overflow-y-auto bg-surface-secondary text-left max-sm:h-full sm:mx-7 sm:my-8 sm:max-w-2xl lg:max-w-5xl xl:max-w-7xl"
|
||||
className="gap-0 overflow-hidden overflow-y-auto bg-surface-secondary p-0 text-left max-sm:h-full sm:mx-7 sm:my-8 sm:max-w-2xl lg:max-w-5xl xl:max-w-7xl"
|
||||
style={{ minHeight: '610px' }}
|
||||
>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,66 +1,95 @@
|
|||
/**
|
||||
* Theme tokens configurable at deploy time. Each entry is the `IThemeRGB` key
|
||||
* without its `rgb-` prefix, and maps to `REACT_APP_THEME_<UPPER_SNAKE>`.
|
||||
* Keep this list in sync with `IThemeRGB` so every applied CSS variable stays
|
||||
* reachable from the environment.
|
||||
*/
|
||||
const THEME_TOKENS = [
|
||||
'text-primary',
|
||||
'text-secondary',
|
||||
'text-secondary-alt',
|
||||
'text-tertiary',
|
||||
'text-warning',
|
||||
'text-destructive',
|
||||
|
||||
'link',
|
||||
'link-hover',
|
||||
'link-visited',
|
||||
'accent-primary',
|
||||
'accent-primary-hover',
|
||||
|
||||
'ring-primary',
|
||||
|
||||
'header-primary',
|
||||
'header-hover',
|
||||
'header-button-hover',
|
||||
|
||||
'surface-active',
|
||||
'surface-active-alt',
|
||||
'surface-hover',
|
||||
'surface-hover-alt',
|
||||
'surface-primary',
|
||||
'surface-primary-alt',
|
||||
'surface-primary-contrast',
|
||||
'surface-secondary',
|
||||
'surface-secondary-alt',
|
||||
'surface-tertiary',
|
||||
'surface-tertiary-alt',
|
||||
'surface-dialog',
|
||||
'surface-submit',
|
||||
'surface-submit-hover',
|
||||
'surface-destructive',
|
||||
'surface-destructive-hover',
|
||||
'surface-chat',
|
||||
'surface-inverted',
|
||||
'surface-inverted-hover',
|
||||
'text-inverted',
|
||||
'surface-fixed',
|
||||
'surface-fixed-hover',
|
||||
'text-fixed',
|
||||
|
||||
'border-light',
|
||||
'border-medium',
|
||||
'border-medium-alt',
|
||||
'border-heavy',
|
||||
'border-xheavy',
|
||||
'border-destructive',
|
||||
|
||||
'status-success',
|
||||
'status-success-subtle',
|
||||
'status-success-border',
|
||||
'status-info',
|
||||
'status-info-subtle',
|
||||
'status-info-border',
|
||||
'status-warning',
|
||||
'status-warning-subtle',
|
||||
'status-warning-border',
|
||||
'status-error',
|
||||
'status-error-subtle',
|
||||
'status-error-border',
|
||||
'status-neutral',
|
||||
'status-neutral-subtle',
|
||||
'status-neutral-border',
|
||||
|
||||
'brand-purple',
|
||||
|
||||
'presentation',
|
||||
];
|
||||
|
||||
const toEnvName = (token) => `REACT_APP_THEME_${token.toUpperCase().replace(/-/g, '_')}`;
|
||||
|
||||
/**
|
||||
* Loads theme configuration from environment variables
|
||||
* @returns {import('@librechat/client').IThemeRGB | undefined}
|
||||
*/
|
||||
export function getThemeFromEnv() {
|
||||
// Check if any theme environment variables are set
|
||||
const hasThemeEnvVars = Object.keys(process.env).some((key) =>
|
||||
key.startsWith('REACT_APP_THEME_'),
|
||||
);
|
||||
|
||||
if (!hasThemeEnvVars) {
|
||||
return undefined; // Use default themes
|
||||
}
|
||||
|
||||
// Build theme object from environment variables
|
||||
const theme = {};
|
||||
|
||||
// Helper to get env value with prefix
|
||||
const getEnv = (key) => process.env[`REACT_APP_THEME_${key}`];
|
||||
|
||||
// Text colors
|
||||
if (getEnv('TEXT_PRIMARY')) theme['rgb-text-primary'] = getEnv('TEXT_PRIMARY');
|
||||
if (getEnv('TEXT_SECONDARY')) theme['rgb-text-secondary'] = getEnv('TEXT_SECONDARY');
|
||||
if (getEnv('TEXT_TERTIARY')) theme['rgb-text-tertiary'] = getEnv('TEXT_TERTIARY');
|
||||
if (getEnv('TEXT_WARNING')) theme['rgb-text-warning'] = getEnv('TEXT_WARNING');
|
||||
|
||||
// Link and accent colors
|
||||
if (getEnv('LINK')) theme['rgb-link'] = getEnv('LINK');
|
||||
if (getEnv('LINK_HOVER')) theme['rgb-link-hover'] = getEnv('LINK_HOVER');
|
||||
if (getEnv('LINK_VISITED')) theme['rgb-link-visited'] = getEnv('LINK_VISITED');
|
||||
if (getEnv('ACCENT_PRIMARY')) theme['rgb-accent-primary'] = getEnv('ACCENT_PRIMARY');
|
||||
if (getEnv('ACCENT_PRIMARY_HOVER'))
|
||||
theme['rgb-accent-primary-hover'] = getEnv('ACCENT_PRIMARY_HOVER');
|
||||
|
||||
// Surface colors
|
||||
if (getEnv('SURFACE_PRIMARY')) theme['rgb-surface-primary'] = getEnv('SURFACE_PRIMARY');
|
||||
if (getEnv('SURFACE_SECONDARY')) theme['rgb-surface-secondary'] = getEnv('SURFACE_SECONDARY');
|
||||
if (getEnv('SURFACE_TERTIARY')) theme['rgb-surface-tertiary'] = getEnv('SURFACE_TERTIARY');
|
||||
if (getEnv('SURFACE_SUBMIT')) theme['rgb-surface-submit'] = getEnv('SURFACE_SUBMIT');
|
||||
if (getEnv('SURFACE_SUBMIT_HOVER'))
|
||||
theme['rgb-surface-submit-hover'] = getEnv('SURFACE_SUBMIT_HOVER');
|
||||
if (getEnv('SURFACE_DESTRUCTIVE'))
|
||||
theme['rgb-surface-destructive'] = getEnv('SURFACE_DESTRUCTIVE');
|
||||
if (getEnv('SURFACE_DESTRUCTIVE_HOVER'))
|
||||
theme['rgb-surface-destructive-hover'] = getEnv('SURFACE_DESTRUCTIVE_HOVER');
|
||||
if (getEnv('SURFACE_DIALOG')) theme['rgb-surface-dialog'] = getEnv('SURFACE_DIALOG');
|
||||
if (getEnv('SURFACE_CHAT')) theme['rgb-surface-chat'] = getEnv('SURFACE_CHAT');
|
||||
|
||||
// Border colors
|
||||
if (getEnv('BORDER_LIGHT')) theme['rgb-border-light'] = getEnv('BORDER_LIGHT');
|
||||
if (getEnv('BORDER_MEDIUM')) theme['rgb-border-medium'] = getEnv('BORDER_MEDIUM');
|
||||
if (getEnv('BORDER_HEAVY')) theme['rgb-border-heavy'] = getEnv('BORDER_HEAVY');
|
||||
if (getEnv('BORDER_XHEAVY')) theme['rgb-border-xheavy'] = getEnv('BORDER_XHEAVY');
|
||||
|
||||
// Brand colors
|
||||
if (getEnv('BRAND_PURPLE')) theme['rgb-brand-purple'] = getEnv('BRAND_PURPLE');
|
||||
|
||||
// Header colors
|
||||
if (getEnv('HEADER_PRIMARY')) theme['rgb-header-primary'] = getEnv('HEADER_PRIMARY');
|
||||
if (getEnv('HEADER_HOVER')) theme['rgb-header-hover'] = getEnv('HEADER_HOVER');
|
||||
|
||||
// Presentation
|
||||
if (getEnv('PRESENTATION')) theme['rgb-presentation'] = getEnv('PRESENTATION');
|
||||
const theme = THEME_TOKENS.reduce((acc, token) => {
|
||||
const value = process.env[toEnvName(token)];
|
||||
if (value) {
|
||||
acc[`rgb-${token}`] = value;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.keys(theme).length > 0 ? theme : undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,32 @@ describe('getThemeFromEnv', () => {
|
|||
'rgb-accent-primary-hover': '13 14 15',
|
||||
});
|
||||
});
|
||||
|
||||
it('loads status, inverted, fixed and destructive colors', () => {
|
||||
process.env.REACT_APP_THEME_STATUS_ERROR = '1 2 3';
|
||||
process.env.REACT_APP_THEME_STATUS_SUCCESS_SUBTLE = '4 5 6';
|
||||
process.env.REACT_APP_THEME_STATUS_NEUTRAL_BORDER = '7 8 9';
|
||||
process.env.REACT_APP_THEME_TEXT_DESTRUCTIVE = '10 11 12';
|
||||
process.env.REACT_APP_THEME_BORDER_DESTRUCTIVE = '13 14 15';
|
||||
process.env.REACT_APP_THEME_SURFACE_INVERTED = '16 17 18';
|
||||
process.env.REACT_APP_THEME_TEXT_INVERTED = '19 20 21';
|
||||
process.env.REACT_APP_THEME_SURFACE_FIXED_HOVER = '22 23 24';
|
||||
process.env.REACT_APP_THEME_TEXT_FIXED = '25 26 27';
|
||||
|
||||
expect(getThemeFromEnv()).toEqual({
|
||||
'rgb-status-error': '1 2 3',
|
||||
'rgb-status-success-subtle': '4 5 6',
|
||||
'rgb-status-neutral-border': '7 8 9',
|
||||
'rgb-text-destructive': '10 11 12',
|
||||
'rgb-border-destructive': '13 14 15',
|
||||
'rgb-surface-inverted': '16 17 18',
|
||||
'rgb-text-inverted': '19 20 21',
|
||||
'rgb-surface-fixed-hover': '22 23 24',
|
||||
'rgb-text-fixed': '25 26 27',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns undefined when no theme variables are set', () => {
|
||||
expect(getThemeFromEnv()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -235,8 +235,15 @@ REACT_APP_THEME_SURFACE_PRIMARY=255 255 255
|
|||
REACT_APP_THEME_SURFACE_SUBMIT=4 120 87
|
||||
REACT_APP_THEME_LINK=37 99 235
|
||||
REACT_APP_THEME_ACCENT_PRIMARY=18 110 107
|
||||
REACT_APP_THEME_STATUS_ERROR=185 28 28
|
||||
REACT_APP_THEME_STATUS_ERROR_SUBTLE=254 226 226
|
||||
REACT_APP_THEME_STATUS_ERROR_BORDER=252 165 165
|
||||
```
|
||||
|
||||
Every `IThemeRGB` key is configurable this way: drop the `rgb-` prefix and
|
||||
upper-snake-case the rest, so `rgb-status-error-border` becomes
|
||||
`REACT_APP_THEME_STATUS_ERROR_BORDER`.
|
||||
|
||||
### 2. Create a Theme Loader
|
||||
|
||||
```tsx
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue