LibreChat/client
Marco Beretta c4357fc9e3
📐 feat: Match the Message Column to the Composer (#14851)
* feat: Match the message column to the composer width

Give messages the same max-width and horizontal padding as ChatForm,
reserve the scrollbar gutter on the composer wrapper, and drop the
65ch prose cap so the body fills that column.

* style: Drop the assistant avatar gutter

Keep the icon and provider name on the same left edge as the message
body. Mid-message author headers and steer bubbles no longer outdent
past a column that no longer exists.

* style: Reveal the timestamp on the message header bar

Put the icon, provider name, and datetime on one full-width row, and
show the time only when the message is hovered or focused. CSS on
.message-render wins the hover hide that Tailwind group-hover lost.

* style: Align scroll-to-bottom with the chat column

Sit the control in the same padded column as the composer, swap the
hard-coded disc for a themed outline Button, and fade it in on an
8px rise instead of a scale pop.

* feat: Crossfade the provider name to the model on hover

Swap the assistant header label to the real model name when the
provider is hovered or focused. Skip agent_* document ids so the
hover text is only a model name.

* fix: Reserve the message column gutter without clipping the composer

`scrollbar-gutter: stable` only holds its band back while the element is a
scroll container, and a scroll container clips. Wrapping the composer in one
put the in-flight steer overlay outside the clip: it is painted above the
composer's top edge, so for the whole run a submitted steer was invisible and
its cancel unreachable. The scroll-to-bottom wrapper had the same problem in
a smaller way, cutting off the button's focus ring.

Reserve the same band with padding instead, sized by the width the app already
gives its own scrollbars, so both columns still line up with the messages
without either becoming a scroll container.

Also scopes the header label crossfade to the two labelled spans, and drops
its `:focus-within` rules, which no focusable descendant can ever trigger.

* fix: Keep document ids out of the header and name the model to screen readers

An Assistants-endpoint message keys the assistant map by `assistant.id`, so
its `model` field holds an `asst_` id, not a model name. The header label only
skipped `agent_`, so it crossfaded the assistant's name into an internal id.
Skip both prefixes, and offer `assistant.model` ahead of the message field in
the callers that already resolved the assistant.

The crossfade itself is pointer-only: the model span is `aria-hidden` and
nothing in the label can take focus, so keyboard and screen reader users had
no path to the value at all. Carry the model in text that never hides, which
puts it in the header's accessible name alongside the author and the time.

* refactor: Own the header crossfade in the component

The provider-to-model crossfade lived in global CSS even though HeaderLabel
is its only consumer. Tailwind expresses the whole effect: a named group for
the hover scope, one grid cell shared by both labels, and the existing resize
duration and easing variables. Reduced motion now follows the same
motion-reduce convention as the rest of the client.

* fix: Return a defined model name from the header lookup

Array.find over nullable candidates widens the return to include null, which
tsc rejects against the declared string | undefined. Narrow with a predicate
and sort the imports the pre-commit hook rewrote.

* fix: Keep the model reachable by keyboard and the scroll button inert

The header crossfade was pointer-only, so a sighted keyboard user never saw
the model name; the screen-reader copy covered announcement but not sight.
Focusing anything in the message row now swaps the label too, the same hook
the timestamp already reveals itself with.

The scroll-to-bottom wrapper spans the column and stays inert so it never
swallows clicks meant for the thread, which left the button to opt back into
pointer events. A descendant that opts in is hit-testable however its parent
paints, so the transition classes could not hold the control inert as they
claimed: the button took clicks while invisible. Gate the opt-in on the enter
transition settling and drop the declarations that never applied.

* fix: Measure the scrollbar gutter and keep the scroll button unreachable

The spacer assumed the gutter was the `::-webkit-scrollbar` width. Blink and
WebKit honour that rule, Firefox ignores it and sizes the band itself, and an
overlay scrollbar reserves nothing at all, so on those the composer and the
scroll-to-bottom control sat off the messages they are supposed to line up
with. Measure what the message column actually holds back and publish it for
the spacer to read, leaving the token as the pre-measurement fallback.

Gating the scroll button on pointer events alone also left it enabled, so it
kept its place in the tab order and answered Enter while invisible. Disable it
until the same gate opens, and hold its opacity so being briefly unreachable
does not dim it on top of the wrapper's own fade.
2026-08-15 07:19:34 +02:00
..
public 🪢 feat: Langfuse Fanout Connection Setting (#14108) 2026-07-29 18:33:10 -04:00
scripts
src 📐 feat: Match the Message Column to the Composer (#14851) 2026-08-15 07:19:34 +02:00
sw 🛟 fix: Auto-Recover from Stale Service Worker Assets After Deploys (#13686) 2026-06-11 11:57:06 -04:00
test 🔒 chore: Upgrade react-router-dom to v7.18.2 (security) (#14582) 2026-08-01 17:19:52 -04:00
babel.config.cjs
check_updates.sh
index.html 📡 feat: add rum browser page-load diagnostics (#14106) 2026-07-05 11:32:53 -04:00
jest.config.cjs 🚀 chore: Prepare v0.8.8-rc1 (#14394) 2026-08-14 03:24:59 -04:00
jest.resolver.cjs ⬆️ chore: Migrate off deprecated @ariakit/react-core to @ariakit/react-components (#13940) 2026-06-24 23:13:57 -04:00
nginx.conf 🐳 feat: Bundle Admin Panel in Docker Compose Stacks (#13876) 2026-06-22 16:59:08 -04:00
package.json 🚀 chore: Prepare v0.8.8-rc1 (#14394) 2026-08-14 03:24:59 -04:00
postcss.config.cjs
tailwind.config.cjs 🎨 feat: Color the Context Gauge by Category and Collapse its Breakdown (#14855) 2026-08-14 22:46:10 -04:00
tsconfig.json 👷 ci: Type-check the Client Workspace (#13560) 2026-06-06 18:40:31 -04:00
vite.config.ts 🎨 refactor: adopt the @librechat/client design system (semantic color tokens + component migration) (#13879) 2026-08-06 09:15:17 -04:00