Skip to content

fix(site/src/pages/AgentsPage): normalize chat menus to a kebab icon and matching items#26588

Open
tracyjohnsonux wants to merge 5 commits into
mainfrom
tj/normalize-agent-chat-menus
Open

fix(site/src/pages/AgentsPage): normalize chat menus to a kebab icon and matching items#26588
tracyjohnsonux wants to merge 5 commits into
mainfrom
tj/normalize-agent-chat-menus

Conversation

@tracyjohnsonux

@tracyjohnsonux tracyjohnsonux commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

The chat title menu used a horizontal ellipsis (meatball) and a different set of items than the sidebar chat-row menu. Designers asked to normalize both menus on the sidebar shape since the sidebar is the more recent design.

The chat top bar now uses the same EllipsisVerticalIcon (kebab) as the sidebar row and exposes the same items in the same order: Pin/Unpin agent, Rename chat, Archive agent, and Archive & delete workspace. Labels are sentence case throughout. The standalone "Generate new title" item is removed because the rename dialog's Generate button covers the same workflow, which is what the sidebar already exposes. The kebab now sits inline with the title rather than off to the right, so it reads as a title menu instead of a global action.

Both menus render from a single ChatActionsMenuItems component so they cannot drift in the future. The shared component is polymorphic over Item and Separator, which lets the sidebar drive both its kebab (DropdownMenu) and its right-click context menu (ContextMenu) from the same JSX.

Implementation notes
  • ChatActionsMenuItems.tsx: new shared component. Renders the Pin/Unpin, Rename, Archive, Archive & delete workspace, and Unarchive items off a flat set of flags (isArchived, isPinned, isChildChat, hasWorkspace, isArchiving) and zero-arg handlers, plus polymorphic Item/Separator components.
  • ChatTopBar.tsx: replace the inline EllipsisIcon + duplicated dropdown body with the kebab trigger and a <ChatActionsMenuItems> call. Move the dropdown trigger into the title area so it sits next to the title text; switch DropdownMenuContent to align="start" so the menu opens flush with the trigger's left edge. Replace the old onRegenerateTitle/isRegenerateTitleDisabled props with onPinAgent, onUnpinAgent, onOpenRenameDialog, isPinned, and isChildChat.
  • ChatTreeNode.tsx: delete the inline renderMenuItems helper and call <ChatActionsMenuItems> from both the DropdownMenuContent and the ContextMenuContent. Pin handlers are pre-bound to chat.id; onOpenRenameDialog is pre-bound to the chat object.
  • AgentsPageView.tsx: lift the rename-chat dialog state up here and expose it through AgentsOutletContext.onOpenRenameDialog, so the sidebar row menu and the chat top bar open the same dialog instance.
  • ChatsSidebar.tsx: accept optional controlled chatPendingRename/onChatPendingRenameChange props with internal-state fallback (controlled-or-uncontrolled pattern). Existing stories and tests are untouched.
  • AgentChatPage.tsx: consume requestPinAgent, requestUnpinAgent, and onOpenRenameDialog from the outlet context, and pass new handlePinAgentAction, handleUnpinAgentAction, handleOpenRenameDialog, isPinned, and isChildChat props through AgentChatPageView to the top bar.
  • Stories: ChatTopBar.stories.tsx replaces the GenerateTitle story with RenameChatItem, PinAgentItem, UnpinAgentItem, and ChildChatHidesPinAction. Updated label expectations to sentence case across stories. AgentChatPage.stories.tsx updated for the new "Archive agent" label.
Decision log
  • Source of truth. Per the designer, the sidebar menu is the canonical list; the chat title menu is what should change.
  • One menu body. A shared ChatActionsMenuItems component drives both menus so they cannot drift. Reviewers only need to read the menu items once.
  • Icon parity. Both menus now use EllipsisVerticalIcon (kebab). The old meatball was the only EllipsisIcon use in this surface.
  • Kebab position. Moved next to the title so the kebab reads as a per-chat action against the title, not a global top-bar action. Share and Toggle-panel stay on the right of the bar.
  • "Generate new title" removed, not preserved as extra. Keeping it would have re-introduced the inconsistency the designer is asking to remove. The rename dialog's Generate button already runs the propose flow and lets the user accept or edit the suggestion. The auto-regenerate code path (requestRegenerateTitle, regeneratingTitleChatIds, the title spinner) is left in place because removing it expands the diff and the regenerate plumbing may be re-used; only the menu trigger is gone.
  • Single dialog instance. Two separate RenameChatDialog instances would have worked, but lifting the state to AgentsPageView keeps a single source of truth and lets the top bar open the dialog the user already knows. ChatsSidebar keeps internal-state fallback so existing stories and tests do not need to thread state.
  • isChildChat. Pin/Unpin is hidden for child chats to match ChatTreeNode, which only renders the Pin item when !isChildNode.

Opened by Coder Agents on behalf of @tracyjohnsonux.

…and matching items

The chat title menu used a horizontal ellipsis (meatball) and a
different set of items than the sidebar chat-row menu. Designers
asked to normalize both menus on the sidebar shape since the
sidebar is the more recent design.

- Switch the chat top bar trigger to the kebab icon
  (EllipsisVerticalIcon) so both menus use the same affordance.
- Match the sidebar's items and order in the top bar: Pin/Unpin
  agent, Rename chat, Archive agent, Archive & delete workspace.
  Sentence case throughout.
- Lift the rename-chat dialog state up to AgentsPageView so a
  single dialog instance is opened from either the sidebar row
  menu or the chat title menu via the outlet context. ChatsSidebar
  keeps internal-state fallback so existing stories and tests are
  unchanged.
- Remove the standalone "Generate new title" item; the rename
  dialog's Generate button covers the same workflow and is what
  the sidebar already exposes.
Moves the agent actions kebab menu from the right-hand actions cluster
into the title area, sitting immediately after the title text. The
dropdown now aligns to its trigger's left edge so it reads as a title
menu rather than a global action.
Extract the per-chat actions menu body into ChatActionsMenuItems so the
sidebar's kebab/right-click menu and the chat top bar's kebab render the
same JSX. The polymorphic Item/Separator props let the same component
fill either a DropdownMenu or a ContextMenu. Both call sites now drive
visibility off the same flags and handlers, so future changes can not
drift between the two menus.
@tracyjohnsonux

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-06-22 23:52 UTC by @tracyjohnsonux
Spend: $56.73 / $100.00

Review history
  • R1 (2026-06-22): 15 reviewers, 4 Nit, 1 P2, 3 P3, COMMENT. Review
  • R2 (2026-06-23): 7 reviewers, 4 Nit, 1 P2, 3 P3, APPROVE. Review

deep-review v0.9.0 | Round 2 | fbf31f2..19b5d74

Last posted: Round 2, 8 findings (1 P2, 3 P3, 4 Nit), APPROVE. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Author fixed (19b5d74) ChatActionsMenuItems.tsx:96 Orphan separator when no pin/rename items render above destructive group R1 Netero Yes
CRF-2 P2 Author fixed (19b5d74) AgentChatPage.tsx:1683 isChildChat derived from async parent query; race window lets child chats be pinned R1 Hisoka P2, Mafuuu P2 Yes
CRF-3 P3 Author fixed (19b5d74) ChatTopBar.tsx:227 Top bar never passes isArchiving to shared menu; destructive items not disabled during archive-in-flight R1 Mafuuu P3, Zoro P3, Nami Note, Meruem Note Yes
CRF-4 P3 Author fixed (19b5d74) AgentChatPageView.tsx:189 Section comment "Archive actions" now covers pin/rename/child-chat props R1 Leorio Yes
CRF-5 Nit Author fixed (19b5d74) ChatActionsMenuItems.tsx:46 Several doc comments restate type signatures or prop names R1 Gon Yes
CRF-6 Nit Author fixed (19b5d74) AgentChatPage.tsx:1300 handleOpenRenameDialog missing Action suffix used by neighbor handlers R1 Gon Yes
CRF-7 Nit Author fixed (19b5d74) AgentsPageView.tsx:4 Redundant named import of Chat alongside namespace import R1 Robin Yes
CRF-8 Nit Author fixed (19b5d74) ChatTopBar.stories.tsx:273 PinAgentItem/UnpinAgentItem stories omit cross-state negative assertions R1 Bisky Yes
CRF-9 Note Dropped by orchestrator (PR decision log acknowledges intentional retention) AgentsPageView.tsx:30 onRegenerateTitle on outlet context now dead code R1 Zoro No
CRF-10 Note Dropped by orchestrator (pre-existing behavior, not introduced by PR) ChatTopBar.tsx:211 Kebab renders before chat data loads R1 Hisoka No
CRF-11 Note Dropped by orchestrator (standard React pattern, one caller) ChatsSidebar.tsx:118 Controlled/uncontrolled rename uses implicit prop pairing R1 Meruem No

Contested and acknowledged

(none)

Round log

Round 1

Panel (15 reviewers: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Nami, Ging-ts, Ging-react, Gon, Leorio, Zoro, Robin, Meruem, Komugi, Kite). 1 P2, 3 P3, 4 Nit, 3 Note (3 dropped). Reviewed against 9b6dc4a..f914832.

Round 2

Churn guard: PROCEED. 8/8 open findings addressed in 19b5d74. Panel (7 reviewers: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Nami, Meruem). 0 new findings. All R1 fixes verified correct. Reviewed against fbf31f2..19b5d74.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@tracyjohnsonux tracyjohnsonux marked this pull request as ready for review June 22, 2026 23:15

@coder-agents-review coder-agents-review Bot 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.

Clean extraction of the shared ChatActionsMenuItems component, well-motivated by the design drift it prevents. The polymorphic Item/Separator slot pattern sits naturally on the existing DropdownMenu/ContextMenu shared styling. The controlled-or-uncontrolled rename dialog and the single-dialog-instance approach are both sound. The story coverage is thorough, especially the negative assertions in ArchivedWithUnarchive and ChildChatHidesPinAction.

Severity counts: 1 P2, 3 P3, 4 Nit.

The P2 is a race window where child chats can be pinned from the top bar because isChildChat waits on a secondary network fetch when the answer is already available from the primary query.

"The component already computes the right value at line 759: const parentChatID = getParentChatID(chatQuery.data). The fix is isChildChat={parentChatID !== undefined}." (Hisoka)

The three P3s are: an orphan <Separator /> when no non-destructive items render, the top bar not threading isArchiving (undermining the 'cannot drift' goal on day one), and a mislabeled section comment.


site/src/pages/AgentsPage/AgentChatPageView.tsx:189

P3 [CRF-4] // Archive actions. now labels a block containing pin handlers, rename handler, isPinned, isChildChat, and isRegeneratingTitle, none of which are archive actions. Someone scanning this 80-prop interface for the pin handler will read "Archive actions", trust the grouping, and skip the block.

"Rename the section or split it. Something like // Chat actions. if you want one group, or add a second // Pin & rename actions. break above the new props." (Leorio)

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/AgentChatPage.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatActionsMenuItems.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatTopBar.tsx
Comment thread site/src/pages/AgentsPage/components/ChatActionsMenuItems.tsx Outdated
Comment thread site/src/pages/AgentsPage/AgentChatPage.tsx Outdated
Comment thread site/src/pages/AgentsPage/AgentsPageView.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatTopBar.stories.tsx
- Derive isChildChat from getParentChatID on the primary chat query so
  Pin agent does not flicker on for child chats while the parent chat
  fetch is in flight (CRF-2, P2). The component already computes the
  same value used elsewhere.
- Conditionally render the separator in ChatActionsMenuItems so it
  never appears alone above 'Archive agent' when no items render above
  it (CRF-1).
- Thread isArchiving from the outlet context through ChatTopBar to the
  shared menu so the top bar's destructive items disable in lockstep
  with the sidebar's during an archive request (CRF-3).
- Rename the misleading '// Archive actions.' section comment in
  AgentChatPageView to '// Chat actions.' since the block now also
  groups pin, rename, and regenerating-title state (CRF-4).
- Drop doc comments that restate the type signature in
  ChatActionsMenuItems and AgentsPageView (CRF-5).
- Rename handleOpenRenameDialog to handleOpenRenameDialogAction to
  match the Action suffix used by the other handlers in the block
  (CRF-6).
- Use TypesGen.Chat throughout AgentsPageView instead of mixing a
  second named import (CRF-7).
- Add negative assertions for the hidden Pin/Unpin label in
  PinAgentItem and UnpinAgentItem stories to mirror the pattern in
  ChildChatHidesPinAction and ArchivedWithUnarchive (CRF-8).
@tracyjohnsonux

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot 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.

All 8 findings from R1 were addressed in 19b5d74. The panel verified each fix against the code:

The P2 race window (CRF-2) is closed: isChildChat now derives from parentChatID, which reads parent_chat_id synchronously from the primary chat query. The isArchiving threading (CRF-3) is complete end-to-end from outlet context through to ChatActionsMenuItems. The orphan separator (CRF-1) is correctly gated. All nits addressed cleanly.

"Fixes address root causes, not symptoms. CRF-2 switches from parentChat !== undefined (dependent on an async secondary query) to parentChatID !== undefined (derived synchronously from the primary chat query)." (Mafu-san)

No new findings in R2. The shared ChatActionsMenuItems component is structurally sound, the controlled/uncontrolled rename dialog pattern works correctly, and the story coverage is thorough with both positive and negative assertions across all conditional branches.

🤖 This review was automatically generated with Coder Agents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant