Commit graph

4889 commits

Author SHA1 Message Date
Marco Beretta
fc3d82d3c3
fix(client): read theme environment variables from the build-time env
getThemeFromEnv read process.env, which vite-plugin-node-polyfills replaces
with an empty shim in the browser, so every REACT_APP_THEME_* value was
dropped and the loader always returned undefined.

Read import.meta.env instead and register the REACT_APP_THEME_ prefix with
Vite so the values are inlined at build time. The env source is now a
parameter, which lets the tests cover the mapping without mutating globals.
2026-08-03 01:43:31 +02:00
Marco Beretta
25b949bbfa
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.
2026-08-03 01:36:28 +02:00
Danny Avila
c1c3d67837
test(e2e): use accessible message action locators 2026-08-02 19:03:13 +02:00
Marco Beretta
364eb282a8
fix(client): expose status tokens to runtime themes and document channel format
Add the status, text-destructive and border-destructive families to IThemeRGB,
IThemeVariables, IThemeColors, mapTheme and the bundled light/dark themes so
ThemeProvider consumers can theme Alert and the status badges instead of falling
back to the stylesheet palette.

Update the theme README to document the channel-triplet contract that the RGB
migration introduced, since the previous examples used complete CSS colors that
now produce invalid declarations.
2026-08-02 18:16:56 +02:00
Marco Beretta
7bb2c3f06c
test(client): update shared component mocks 2026-08-02 18:16:56 +02:00
Marco Beretta
6caadc27e4
fix(client): resolve CI validation failures 2026-08-02 18:16:56 +02:00
Marco Beretta
7fc22139d4
fix(client): use boolean Monaco hover option 2026-08-02 18:16:56 +02:00
Marco Beretta
c48d92080e
fix(client): address semantic theme review feedback 2026-08-02 18:16:56 +02:00
Marco Beretta
d610ce9b33
refactor(client): remove residual shadcn color tokens
The background/primary/primary-foreground/ring and unused chart-* tokens were
retained only for the then-unmigrated Agent Builder. With that panel migrated,
replace the last usages with LibreChat semantic tokens (ring-primary/ring-ring
-> ring-text-primary; bg-primary/text-primary-foreground -> bg-surface-inverted
/text-text-inverted; text-primary -> text-text-primary; bg-background ->
bg-surface-primary) and drop the token definitions from createTailwindColors,
applyTheme, the theme objects, types, and style.css.
2026-08-02 18:16:56 +02:00
Marco Beretta
c53606de65
fix(client): keep Input border static on pointer focus
The pointer-focus override in Field.css used border-color: var(--border-light),
which became an invalid value after the theme moved to RGB channel tokens and
was silently dropped, letting the border fall back to currentColor (text-primary)
on mouse focus. Wrap it in rgb() so mouse focus produces no border, ring, or
outline change; keyboard focus keeps its ring for accessibility.
2026-08-02 18:16:56 +02:00
Marco Beretta
b3617e48c1
refactor(client): migrate Agent Builder and Tool Library to @librechat/client
Swap raw buttons, inputs, labels, textareas and native title tooltips for
the @librechat/client Button/Input/Label/Textarea/TooltipAnchor components
across the Agent Builder panel (SidePanel/Agents) and the Tools
marketplace. Remove heavy input focus rings in favor of subtle borders,
soften dropdown trigger borders, and convert stray shadcn/raw colors in
touched lines to semantic tokens. Localize the tool delete aria-label and
toast messages. Update co-located test mocks to provide the newly-used
Button component.
2026-08-02 18:16:56 +02:00
Marco Beretta
fc5185a1a4
style(client): soften dropdown and settings search inputs
Remove the heavy focus ring on the settings search and the searchable
Dropdown's search input, replacing it with a subtle border-light. Make
the search field background inherit the dropdown surface so it matches in
both light and dark mode, and reduce the Dropdown trigger border from
medium to light.
2026-08-02 18:16:56 +02:00
Marco Beretta
aa1f1e82dc
refactor(client): migrate dialogs, toggles and remaining controls to @librechat/client
Swap behavioral controls for @librechat/client equivalents: HeadlessUI
and legacy dialogs to OGDialog, native checkbox/switch to Checkbox/Switch
(onCheckedChange), and remaining buttons/inputs across Chat, Skills,
Tools, Sharing, Memories and Settings. Convert applicable native title=
tooltips to TooltipAnchor and localize close/scroll aria-labels. Skip
swaps that would break floating-label animations or bespoke select
behavior. Update co-located test mocks to provide the newly-used Button
and cn dependencies.
2026-08-02 18:16:56 +02:00
Marco Beretta
91c40d1907
refactor(client): migrate buttons, inputs and labels to @librechat/client
Swap raw <button>, <input> and <label> elements for the @librechat/client
Button, Input and Label components across Auth, Chat, Conversations,
Endpoints, Nav, Prompts, Skills, Tools and Web. Preserve bespoke geometry
and behavior via cn className merging, keep data-testid/aria wiring, and
localize previously hardcoded aria-labels. Skip swaps that would break
floating-label animations, tiny bespoke controls or inline-text links.
Add com_ui_reload_page key.
2026-08-02 18:16:56 +02:00
Marco Beretta
a1b22b745d
refactor(client): migrate low-risk primitives to @librechat/client
Swap raw <label>, <textarea>, and native title= tooltips for the
@librechat/client Label, Textarea, and TooltipAnchor components across
Agents, Endpoints settings, Export modal, Prompts, Sharing, and Memory
dialogs. Add localization keys (scroll, sibling navigation, none
selected, select var) for the remaining swap waves.
2026-08-02 18:16:24 +02:00
Marco Beretta
f658ffdb3c
fix: prettier formatting after dev rebase 2026-08-02 18:16:24 +02:00
Marco Beretta
6dc3d3b21a
refactor: migrate theme tokens to RGB channels for opacity support
Convert semantic + palette CSS variable values in style.css from hex to bare
'R G B' channel triplets, and emit Tailwind colors as
rgb(var(--token) / <alpha-value>) via createTailwindColors. This makes opacity
modifiers (bg-surface-primary/50, bg-border-medium/60, etc.) resolve correctly
and remain dark-aware, fixing ~26 existing usages that previously fell back to a
hardcoded light hex.

- Wrap direct var(--token) color usages in CSS rules as rgb(var(--token))
  (style.css, Dropdown.css, Tooltip.css) and two inline component styles
- applyTheme writes bare triplets to match the new wrapping
- shadcn tokens (HSL) and the JS palette (hex) are unchanged
2026-08-02 18:16:24 +02:00
Marco Beretta
fc5d3bc949
fix: resolve ESLint and frontend test failures
- Format with prettier (Alert, MCPStatusBadge, ApiKeys, Memory, etc.) after
  --no-verify commits skipped the hook
- Localize the 'Or' auth divider (com_auth_or) instead of a bare literal
- Drop dead InvocationModePicker imports in Skill forms; fix VerifyEmail
  unused arg + useEffect deps
- Revert out-of-scope color edits in legacy Files/VectorStore views that
  carried pre-existing untranslated-string lint debt
- Update Memory tests to assert status-* tokens (text-status-error,
  bg-status-error-subtle) instead of the old hardcoded red classes
2026-08-02 18:16:24 +02:00
Marco Beretta
53269ddf4f
refactor: remove unused CSS rules, dead comments, and duplicate keyframes
Drop ~829 lines of dead styles across style.css (2992->2355) and
mobile.css (323->131): unreferenced classes (legacy token utilities,
orphaned animations, form/prose/scrollbar leftovers), commented-out
blocks, and duplicate/orphaned keyframes. Library-injected (hljs, sandpack,
codemirror, markdown language) and dynamically-applied (scroll-animation,
icon sizes) classes were retained.
2026-08-02 18:16:24 +02:00
Marco Beretta
5fefbaf40f
refactor: tokenize remaining status, neutral and message-text colors
Map the leftover semantic colors to tokens: skill error/dirty states and the
selected-version/selected-skill highlights move to status-warning/status-success,
the global indicator to status-success, and the markdown message text to
text-text-primary. Drop the redundant dark: overrides on the dynamic checkbox,
which the Checkbox primitive already handles.

What remains is intentional and stays raw: categorical color sets (category
icons, principal avatars, per-tool toggle accents), brand marks, the
WCAG-tuned toast severities, code/diagram surfaces, scrims, and text-white on
submit/destructive action surfaces.
2026-08-02 18:16:24 +02:00
Marco Beretta
75bc678ac6
refactor: replace shadcn color vocabulary with semantic tokens
Remove the shadcn/ui color tokens (background, foreground, card, popover,
muted, accent, secondary, destructive, input) and migrate every usage to
LibreChat semantic surface/text/border tokens.

Add surface-inverted/text-inverted for the neutral inverted CTA and
surface-fixed/text-fixed for controls that must not flip with the theme
(favicon chips, QR container, carousel arrows). New tokens are defined once
in style.css (light + dark), createTailwindColors, the theme types,
applyTheme and the default/dark theme objects so they stay overridable at
runtime.

Collapse paired dark: color variants into the dark-aware tokens and tokenize
the remaining raw palette and white/black utilities, mapping status colors to
the status-* tokens and legacy ring-black/ring-white focus rings to
ring-text-primary.

Retain the background, primary and ring tokens, which are still referenced by
the SidePanel/Agents and SidePanel/Builder panels (excluded from this pass).
2026-08-02 18:16:24 +02:00
Marco Beretta
c36f126302
refactor: tokenize token-usage gauge, mic, and oauth countdown status colors 2026-08-02 18:16:24 +02:00
Marco Beretta
7d8a9530a4
refactor: migrate status callout banners to status-subtle/border tokens 2026-08-02 18:16:24 +02:00
Marco Beretta
6030bc3012
refactor: final solid-color cleanup (brand-green accents, neutral grays, error text) 2026-08-02 18:16:24 +02:00
Marco Beretta
c698d57796
refactor: tokenize OAuth, Share, ui, Bookmarks, Tools, Messages, Web, SharePoint colors 2026-08-02 18:16:24 +02:00
Marco Beretta
a2dbea1eb2
refactor: tokenize Auth brand-green accents, Skills, Sharing, Plugins, MCP colors 2026-08-02 18:16:24 +02:00
Marco Beretta
344fbd6dad
refactor: add accent-primary brand token; tokenize Nav/Input/Prompts/Endpoints colors 2026-08-02 18:16:24 +02:00
Marco Beretta
29395381ea
refactor: tokenize Files and Auth surfaces, text, borders, and CTAs 2026-08-02 18:16:23 +02:00
Marco Beretta
d407859a4e
refactor: add semantic link color token and migrate hyperlinks to it 2026-08-02 18:16:23 +02:00
Marco Beretta
ac4f49e5b9
refactor: tokenize Chat message content, tool output, and file UI colors 2026-08-02 18:16:23 +02:00
Marco Beretta
e29a0b97e0
refactor: tokenize Chat menus, popovers, and message surfaces 2026-08-02 18:16:23 +02:00
Marco Beretta
81defcbf7c
refactor: add soft status-border token layer for Alert and lighten dark status foregrounds 2026-08-02 18:16:23 +02:00
Marco Beretta
d7430634c6
fix: explicit type annotations on Alert for isolatedDeclarations
@librechat/client builds with tsdown --isolatedDeclarations, which requires
exported consts to have explicit type annotations (TS9010). Annotate
alertVariants and Alert to match the Button.tsx pattern.
2026-08-02 18:16:23 +02:00
Marco Beretta
2b81a6de39
refactor: tokenize Chat file-upload table and upload status colors
Tokenize TemplateTable th/td/border classes (surface-primary, border-light,
text-primary/secondary) and FileUpload status colors (text-text-secondary,
text-text-destructive, text-status-success) plus the import button hover.
2026-08-02 18:16:23 +02:00
Marco Beretta
8bb1adc67b
refactor: tokenize Settings danger/destructive buttons
Map the DangerButton, the Data tab destructive actions (RevokeKeys, ClearChats,
DeleteCache), and the DeleteAccount button from bg-red-*/bg-destructive to the
surface-destructive tokens.
2026-08-02 18:16:23 +02:00
Marco Beretta
76b66b224c
refactor: tokenize SidePanel Memories/Parameters/Bookmarks colors
Delete-confirm buttons to surface-destructive tokens (MemoryCardActions,
BookmarkCardActions), drop redundant text-white on submit Buttons (the variant
already sets it), legacy preset button green hover/focus to submit tokens, and
slider hover borders to border-light. Leaves DynamicCheckbox dark overrides for
a separate pass against the Checkbox component.
2026-08-02 18:16:23 +02:00
Marco Beretta
115a968f13
refactor: migrate remaining alert banners and error states to tokens
Migrate the last banners to the Alert component: ResetPassword success,
MessageContent connection error, and MemoryInfo storage-full errors. Tokenize
the Agents ErrorDisplay error state in place (icon badge, headings, message,
retry button) since it's a full error state, not a compact callout. Also
tokenize ResetPassword field-validation errors to text-text-destructive
(fixes the low-contrast dark:text-red-900).
2026-08-02 18:16:23 +02:00
Marco Beretta
95f7717329
feat: add Alert component and migrate alert banners to it
Add a reusable Alert component (@librechat/client) with error/success/warning/
info/neutral variants backed by the status-color tokens, default per-variant
icons, and role=alert. Migrate the duplicated colored-div banners to it:
Auth ErrorMessage, RequestPasswordReset success, and the identical error boxes
in ToolSelectDialog, AssistantToolsDialog, and MCPToolSelectDialog.
2026-08-02 18:16:23 +02:00
Marco Beretta
2357bb1f11
refactor: migrate status badges to semantic status-color tokens
Migrate the genuine status badges to the status-* tokens: MCPConfigDialog
connection pills (info/warning/neutral/error/success + dot), MemoryUsageBadge
usage levels, and DialogImage quality badge (also gains dark-mode support it
previously lacked). Removes hardcoded colors and dark: twins.
2026-08-02 18:16:23 +02:00
Marco Beretta
107146a21c
feat: add semantic status-color tokens; migrate MCP status badge
Add a status-color layer (status-{success|info|warning|error|neutral} plus
-subtle variants) to style.css and the unified createTailwindColors map, with a
blue palette for the info hue. Migrate MCPStatusBadge (badges + dots) and
MCPCardActions to the new tokens, removing all hardcoded status colors and
dark: twins. Status colors are now themeable like the rest of the system.
2026-08-02 18:16:23 +02:00
Marco Beretta
9a83129d4c
refactor: replace hardcoded colors in sidebar conversation list with tokens
Migrate the Conversations sidebar section to semantic tokens: focus rings to
ring-text-primary (keeps >=3:1 contrast in both modes; the mid-gray ring would
fail WCAG 1.4.11 on dark), the active-conversation indicator and hover-fade
gradient to surface/text tokens, and the pagination controls. Removes every
dark: color twin; no behavior change.
2026-08-02 18:16:23 +02:00
Marco Beretta
837ddedb95
refactor: unify Tailwind color tokens into a single source
Both the client SPA and @librechat/client Tailwind configs now consume one
createTailwindColors() map, eliminating config drift. Fixes the package-side
build along the way: shadcn tokens are wrapped in hsl(), the broken opacity
helper is removed, and text-destructive/border-destructive/switch-unchecked
plus the gray/green palettes are included.
2026-08-02 18:16:23 +02:00
Danny Avila
cdb60e74c2
⌨️ fix: Honor defaultPrevented in Global Shortcut Dispatch (#14570)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
* ⌨️ fix: Honor defaultPrevented in Global Shortcut Dispatch

* ⌨️ fix: Order-Independent Shortcut Yield via Window Listener

* 📝 fix: Align Remaining Shortcut Contract Docs with Window Listener

* 🧪 test: e2e Yield Contract Coverage for Global Shortcut Dispatch

* 🧪 fix: Match Real Generation POST Path in Shortcut e2e
2026-08-02 08:08:12 -04:00
Danny Avila
cdf437dc5b
🪺 fix: Keep Preempt-Abandoned Siblings Nested, Make Message Tree Order-Robust (#14587)
* 🪺 fix: Keep Preempt-Abandoned Siblings Nested, Make Message Tree Order-Robust

* 🔗 fix: Sever Cycle Back-Edges So the Returned Message Tree Is Acyclic

* 🧪 fix: Satisfy TFile in buildTree Spec fileMap Fixture

* 🌲 fix: Assert Repaired Trees in convoStructure Specs, Uncharge Self-Parent Edges

* 📌 feat: Identity-Stable Sibling Selection Across Background Tree Churn

* 🎭 test: E2E Coverage for Thread Fold and Sibling Selection Invariants

* 🔑 fix: Treat Newest-Sibling Re-Key as Hydration, Not a New Branch

* 🧭 fix: Rebind Sibling Selection Per Parent, Detect Appends by Membership
2026-08-02 07:04:52 -04:00
Danny Avila
2d606a9783
🧹 chore: Migrate Legacy Duplicate Code Files Blocking Dedupe Index (#14593)
Atomic file claiming (#11675) added a unique partial index on
(filename, conversationId, context, tenantId) for execute_code outputs.
Records written before it inserted a new document per regeneration, so
any deployment that re-ran a cell producing the same filename carries
duplicates the index cannot span: Mongo aborts the build with E11000 and
the constraint is silently absent — the claim path still works, but
without its database-level guard against concurrent inserts.

Adds config/migrate-code-file-duplicates.js to normalize that legacy
data, following the existing migration conventions (dry-run default,
--batch-size, runAsSystem for cross-tenant scans).

Renames rather than deletes: each duplicate is a distinct stored object,
typically still referenced by a message attachment, so removing one
would strip a real artifact from a user's history. The newest record
keeps the canonical name — matching the claim path's latest-write-wins
behavior — and older copies gain a ' (n)' suffix that skips names
already taken in the conversation. Attachments embed their own filename,
so rendered history is unchanged.

After a successful apply the script builds the index directly (targeted
createIndex, not syncIndexes) so the operator learns immediately whether
the constraint is now in place.
2026-08-02 06:41:21 -04:00
Danny Avila
928b14f5bc
🔒 fix: Single-Flight MCP OAuth Token Refresh per User/Server (#14596)
* 🔒 fix: Single-Flight MCP OAuth Token Refresh per User/Server

Concurrent refresh-token redemptions (tool-call 401, ping, reconnect
retries, expired-token reads) each replayed the same stored refresh
token at the OAuth token endpoint. RFC 9700 reuse detection treats the
replay as theft and revokes the entire grant family, forcing manual
re-consent every access-token expiry.

MCPTokenStorage.forceRefreshTokens is the choke point every refresh
path converges on; it now single-flights redemptions per
(tenantId, userId, serverName) so concurrent callers share one wire
call and receive the same rotated result. The refresh token is re-read
from storage inside the locked execution — never from a caller
snapshot — so a redemption starting after another refresh completed
uses the rotated token instead of replaying the consumed one.

Fixes #14583

* 🧪 test: Isolate Single-Flight Keys per Test via Unique Server Names

* 🔒 fix: Evict Stalled Refresh Slots, Decouple Waiter Aborts from Shared Redemption

Codex review round 1:
- A redemption that never settles no longer wedges the single-flight
  slot until process restart: a stale-entry timer evicts the map entry
  so later refreshes start fresh, while existing waiters keep their
  promise.
- Caller AbortSignals no longer thread into the shared redemption. An
  impatient waiter (silent refresh's short timeout) resolves its own
  wait with null via a per-waiter race; the shared wire call proceeds
  for everyone else, bounded by transport timeouts plus eviction.

* 🔒 fix: Abort Stalled Refreshes Before Slot Release, Hook Cache Invalidation to Redemption

Codex review round 2:
- The stale timer now aborts the wedged execution instead of deleting
  its slot; the slot frees only once the execution has settled, and an
  abort guard before the token-endpoint call stops a woken pre-wire
  stall from replaying a refresh token a successor already rotated.
- New onRefreshSuccess hook runs inside the shared redemption after
  rotated tokens persist, so the silent-refresh path's mcp_get_tokens
  cache invalidation fires even when the initiating waiter timed out
  before the redemption completed.

* 📝 docs: Record Post-Dispatch Abort Recovery Rationale on Stale-Refresh Valve
2026-08-02 06:39:44 -04:00
Danny Avila
7e74f8eb8c
🪪 fix: Strip Unresolved Header Placeholders at Final Resolution (#14595)
Unresolved {{LIBRECHAT_USER_*}} header templates leaked literally to
upstream providers when user context was missing at resolution time
(e.g. async title generation racing client disposal), letting a gateway
trust LibreChat's own template syntax as an account identity.

resolveHeaders now takes an opt-in stripUnresolved flag that blanks any
resolvable-but-unresolved LIBRECHAT_USER/BODY/OPENID placeholder, enabled
at every final resolution boundary (resolveConfigHeaders, model fetches,
Google init, summarization overrides, azureAssistants init). Staged
passes that resolve again later with more context are left untouched, as
is the async-resolved {{LIBRECHAT_GRAPH_ACCESS_TOKEN}} and unknown names.

titleConvo now resolves headers from the req captured at entry instead of
re-reading this.options.req, which disposeClient nulls concurrently.

Fixes #14580
2026-08-02 06:38:06 -04:00
Danny Avila
ed25ae5b59
🧪 ci: Settle to render-idle before ConversationsSection memo baselines (#14590)
The lazy BookmarkNav's Suspense resolution commits during waitFor's
polling, outside any act scope, so its follow-up render work lands in
React's real scheduler as a macrotask. The single empty async act added
in #14071 only drains microtasks and the act queue, so on slow Windows
shards that work can still be pending when baselines are captured. The
next act flushes pending root work wholesale, so the first stream tick
carries the leftover pass and inflates the tag counter (Expected: 1,
Received: 2). Flush full event-loop turns inside act until two
consecutive turns add no renders, then capture baselines.
2026-08-02 03:38:55 -04:00
Danny Avila
105f0c6236
🧭 fix: Drop v6-Only MemoryRouter future Prop from Skill Markdown Spec (#14588)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
2026-08-01 18:35:09 -04:00
Danny Avila
2fb03118bb
💬 feat: Interim Progress Card for Streaming Q&A Calls (#14576)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
* 💬 feat: Interim Progress Card for Streaming ask_user_question Calls

* 🔍 fix: Match Progress Card Against Every Live Ask Pause, Not Newest Only

*  feat: Hold Streaming Cursor Under Answered Question While Resume Is In Flight
2026-08-01 18:25:53 -04:00