Skip to content

⚙️ feat: Move Chat Actions Into a Header Options Menu - #16775

Merged
berry-13 merged 6 commits into
devfrom
feat/chat-options-menu
Oct 5, 2026
Merged

berry-13 merged 6 commits into
devfrom
feat/chat-options-menu

Conversation

@berry-13

@berry-13 berry-13 commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The chat header's export/share button only offered Share and Export, while the other things you do to the open chat (rename, pin, move to a project, duplicate, archive, delete) were reachable only from its row in the sidebar. The header button is now a three-dots chat options menu for the open chat: Share and Export first, then Rename, Pin/Unpin, Change project, Remove from project (only when the chat is in a project) and Duplicate, then Archive/Unarchive and Delete. The mobile overflow menu uses the same items. Its label is "Chat options", or "Chat options, link active" when the chat has a shared link, and the blue link dot is kept.

The sidebar row menu keeps the quick actions for any row: Pin, Mark unread, Rename (inline), Archive and Delete. Share, Duplicate, Change project and Remove from project now live only in the header, so those four apply to the open chat only. Project assignment from the sidebar is still available by dragging a row onto a project. The sidebar renames inline in its row and the header has no row, so rename in the header opens a small dialog.

The header menu follows the sidebar's rules where they apply: a read-only subagent thread offers only Share and Export, Rename is disabled for a running chat unless the deployment supports title ownership, saving an unchanged automatic title claims ownership, and a temporary chat drops Pin, Change project, Remove from project and Archive because those lists exclude temporary chats.

How it works

useChatOptions builds the menu items and owns the dialogs, reusing the sidebar's mutations, DeleteButton and ProjectButton, and the Share/Export dialog-item contract (hideOnClick: false, item ref, button render, dialog triggerRef). ChatView stays mounted while the route changes, so the hook ties each dialog to the conversation it was opened for, closes the menu and dialogs when the route or the read-only state changes, and checks the open route when a late archive or delete settles. The open chat is read from the freshest cached copy, and a landed pin, project change or archive is also written to the open chat's own state.

DeleteButton gained the delete-conversation-dialog id that the sidebar menu's aria-controls already pointed at, and an optional getCurrentConversationId for callers whose dialog outlives its route. ProjectButton gained an optional onAssigned.

Type of change

  • Feature

Testing

Tested environments/configuration:

Unit tests in jsdom. The header and sidebar menus were also opened in headless Chromium against the dev server (screenshots below) to check which items each menu lists; interactions such as focus return and route changes were not exercised in a browser, and the Playwright specs were not run locally.

Automated tests:

  • lc test on the changed files: useChatOptions.spec.tsx (item order, dialog-item contract, read-only, rename gate, temporary chats, route changes, late archive and delete, stale cache), Rename.spec.tsx, DeleteButton.test.tsx, pinPointCache.spec.tsx, plus the updated ExportAndShareMenu, HeaderMenu, Header.stacking and ConvoOptions.archive specs
  • e2e specs updated for the new label and the trimmed sidebar menu, not run locally; CI passes
  • lc lint, client tsc --noEmit and npm run static-checks -- --against origin/dev clean

Screenshots / recordings

Captured from the running app (dev server on a mock endpoint, light theme), base revision on the left and this branch on the right. Desktop is 1280 px wide, mobile is 390 px wide. The React Query devtools button is hidden in both.

Before After
Header, menu closed Header before Header after
Header menu, desktop Header menu before Header menu after
Sidebar row menu Sidebar menu before Sidebar menu after
Header menu, mobile Mobile menu before Mobile menu after

Light mode only; dark mode was not captured.

Risk / compatibility

Share, Duplicate and Change project can no longer be started from a closed chat's sidebar row. Only English locale keys changed: added com_ui_chat_options and com_ui_chat_options_link_active, removed the two unused export/share keys. DeleteButton is shared with the sidebar, the project list and the keyboard delete dialog; the new props are optional.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors
  • User-facing or complex behavior is documented where necessary
  • Required dependency changes have been merged/published
  • Required documentation PR: N/A

The header's export/share button becomes a chat options menu with Share, Export, Rename, Pin, Change project, Remove from project, Duplicate, Archive and Delete for the open chat. The sidebar row menu keeps Pin, Mark unread, Rename, Archive and Delete.
Copilot AI balanced review requested due to automatic review settings October 5, 2026 10:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-05T12:27:08.646923Z 536ae32 New commits
🔒 Security Review ✅ Completed 2026-10-05T10:32:52.427007Z 3246f94 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@codegraph-librechat codegraph-librechat Bot added 🗺️ Chat Frontend codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🛡️ security review labels Oct 5, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3246f94f34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread client/src/hooks/Chat/useChatOptions.tsx
Comment thread client/src/hooks/Chat/useChatOptions.tsx
Comment thread client/src/components/Chat/Rename.tsx
Comment thread client/src/hooks/Chat/useChatOptions.tsx Outdated
Comment on lines +68 to +71
/** Pin and project changes land in the query cache, not in the chat's own state. */
const { data: cached } = useGetConvoIdQuery(conversationId, { enabled: false });
const current = cached ?? conversation;
const isPinned = current?.pinned === true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the point cache after toggling a pin

For a normally loaded saved chat, cached contains the point-query snapshot and therefore always wins over the mirrored Recoil conversation here. usePinConversationMutation updates conversation-list and pinned caches but does not update [QueryKeys.conversation, conversationId], while this observer is explicitly disabled from refetching, so after a successful pin or unpin the header continues showing the old action and repeats the same write when clicked again; update the point cache from the mutation result or derive the pin state from a source that the mutation updates.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not reproduced. updateConvoInAllQueries, which the pin mutation calls on success, also updates the [conversation, id] point entry when one exists, and the header only reads that entry when it exists. A test that seeds the point entry, pins, and reads it back passes on the unchanged mutation (pinPointCache.spec.tsx), so I kept it as a guard rather than as a regression test. The open chat's state is also updated on success for the case where no point entry exists. Leaving this open in case you have a sequence that still shows a stale label.

Comment thread client/src/hooks/Chat/useChatOptions.tsx
Comment thread client/src/hooks/Chat/useChatOptions.tsx
Limit read-only subagent threads to share and export, apply the sidebar's rename gate for running chats, let an unchanged title claim ownership, keep later navigation after a late archive, hide history-only actions for temporary chats, expose dialog semantics on the dialog items, and fix import sorting.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f02e942ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread client/src/hooks/Chat/useChatOptions.tsx Outdated
… For

ChatView stays mounted across a route change, so dialog flags now carry the conversation id they were opened for and reset when the open chat or the read-only state changes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a2b5331519

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread client/src/hooks/Chat/useChatOptions.tsx
Comment thread client/src/hooks/Chat/useChatOptions.tsx
Comment thread client/src/components/Chat/Rename.tsx Outdated
Read the open route when a delete settles, give the delete dialog the id its menu item controls, return focus to the menu item when a rename saves, and only let a close clear the dialog it belongs to.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45431d676a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread client/src/components/Chat/Rename.tsx
A rename that settles after its dialog unmounted no longer closes the parent state or moves focus to the reused menu item.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73a7319efb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread client/src/hooks/Chat/useChatOptions.tsx Outdated
Comment thread client/src/hooks/Chat/useChatOptions.tsx Outdated
Comment thread client/src/hooks/Chat/useChatOptions.tsx Outdated
The route moves before the chat state does and the menu and dialogs are portaled outside the hidden pane, so they are now dismissed on a route change and gated on it. A delete that settles after another chat opened no longer closes that chat's menu, and the options read the freshest cached copy of the chat instead of an older point entry.
@berry-13
berry-13 merged commit b56de23 into dev Oct 5, 2026
30 checks passed
@berry-13
berry-13 deleted the feat/chat-options-menu branch October 5, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🗺️ Chat Frontend codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🛡️ security review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants