mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-09-04 05:28:30 +00:00
* ♻️ refactor: Extract `useNewChat` as the Single New-Chat Path The new-chat sequence (clear the outgoing conversation's cached messages, invalidate the messages query, reset the conversation atom) existed in three places: the sidebar's `NewChatButton`, the `newChat` keyboard shortcut, and an unrendered `Nav/NewChat` component. Consolidate into `hooks/Chat/useNewChat`. The panel switch stays an optional `onNewChat` callback rather than living in the hook, because `useActivePanel` throws outside `ActivePanelProvider` and the chat header sits outside it — the upcoming header button needs this seam. `useKeyboardShortcuts` consumes the returned `newConversation` so the file still instantiates `useNewConvo` exactly once. Delete `Nav/NewChat`: it was reachable only through its own barrel export, and carried a stale `max-md:hidden` plus a `data-testid` that collided with the sidebar's button. `handleNewChatClick` now also defers on shift-click, so shift-click opens a new window like any other link. `ExpandedPanel.spec` mocks the new hook — it reaches `useNewConvo` by deep path, which escapes the spec's `~/hooks` barrel mock. * ♻️ refactor: Split Header Action Logic Out of Its Buttons Lift the behaviour behind the compare and temporary-chat header buttons into `useMultiConvo` and `useTemporaryChat`, leaving each component as a thin trigger. The upcoming mobile overflow menu needs the same actions as menu items, and the visibility rules (assistants have their own comparison surface; temporary chat can't be toggled mid-thread) have to stay in one place rather than being restated per surface. Add the header's new-chat button, consuming `useNewChat`. It renders as an anchor to `/c/new` so modified clicks still open a tab, and uses a distinct `data-testid` from the sidebar's button so queries can't match both. `useTemporaryChat` toggles through a functional updater, dropping the `useRecoilCallback` that existed only to close over the current value. No visual change yet — the header layout lands next. * 📱 style: Fold the Mobile Header Into Four Targets The mobile header was a horizontally scrolling strip of up to seven controls, each in its own outlined box, so nothing grouped and nothing receded. The overflow was hidden rather than solved: ModelSelector alone is capped at 70vw (273px) and the side clusters need ~130px, which does not fit a 390px phone. Mobile now reads: sidebar toggle, model selector, new chat, ellipsis. Lift the bookmark and export/share menu items into `useBookmarkItems` and `useExportShare`, each returning the items plus the dialog instance the surface must render. Both menus already built `MenuItemProps[]` internally, so the desktop buttons keep their exact markup and simply consume the hook — the two surfaces cannot drift apart. `HeaderMenu` composes those with the compare and temporary-chat actions. Bookmarks nests through `subItems` rather than flattening every tag to the top level, permission gates decide membership, and the trigger does not render when nothing survives — reachable, since export/share self-hides on a new conversation. Layout is one DOM order serving both breakpoints; hidden items generate no flex gap, so each collapses without reordering. Branching is CSS-only: `useMediaQuery` resolves after paint, and the old `isSmallScreen ? <OpenSidebar/> : null` popped the row a frame late on every mount. `overflow-x-auto` is gone. It hid the overflow instead of fixing it, and it is a horizontal-swipe sink the later edge-swipe work needs removed. Presets stays a visible mobile icon for now: `PresetItems` uses Radix's `Close`, which throws outside a Popover root, so folding it needs a controlled + anchored menu and browser verification. Also drops two stray `console.log` calls carried along from the bookmark mutation handlers. * 🩹 fix: Address Codex Findings on the Mobile Header Menu `separate: true` marks an entry as *being* a divider — `DropdownPopup` returns only a `MenuSeparator` for it and drops the item. Setting the flag on Share and on temporary chat therefore deleted those actions whenever an earlier group existed. Push standalone divider entries instead. The spec missed this because its `DropdownPopup` mock rendered every label regardless of the flag. It now mirrors the real contract — dividers replace items, `show: false` entries are dropped — and asserts both actions survive alongside their dividers. Gate the bookmark tags query on the bookmark permission. `HeaderMenu` mounts unconditionally and called `useBookmarkItems` before the permission result applied, so users without `BOOKMARKS:USE` issued a forbidden request on every chat header mount; the old header dodged this by mounting `BookmarkMenu` only after the check. Restore two states the collapsed trigger had dropped: the shared-link indicator and its active-link label, and a visible checked state for temporary chat, which previously only reached assistive tech through `aria-checked` while the old button switched to `bg-surface-active`. Compose both new controls from the shared `Button` primitive with the same override `OpenSidebar` already uses, rather than restating the bordered icon-button recipe locally. * 🐛 fix: Give the Overflow Menu's Share Indicator Its Own Test Id Restoring the shared-link indicator on the mobile trigger reused the id `ExportAndShareMenu` already owns. Both headers stay mounted and are only hidden by CSS, so `getByTestId('header-shared-link-indicator')` matched two elements and `shared-links.spec.ts` failed on a strict-mode violation. Distinct id, matching the new-chat button, which was already separated from the sidebar's for the same reason. |
||
|---|---|---|
| .. | ||
| public | ||
| scripts | ||
| src | ||
| sw | ||
| test | ||
| babel.config.cjs | ||
| check_updates.sh | ||
| index.html | ||
| jest.config.cjs | ||
| jest.resolver.cjs | ||
| nginx.conf | ||
| package.json | ||
| postcss.config.cjs | ||
| tailwind.config.cjs | ||
| tsconfig.json | ||
| vite.config.ts | ||