feat(frontend): tag-style agent playground session bar + chat UI polish - #4983
Conversation
- Replace the editable-card session tabs with a tag/chip bar (status dots, truncation, double-click rename, hover close) rendered via Tabs renderTabBar, so panes and their live useChat streams keep antd mount semantics; the bar's bottom edge now aligns with the config panel header. - Unify per-session run state behind a single source of truth (sessionStatusAtomFamily); derive the inspector's streaming flag from it instead of a competing boolean. - Move the per-message trace/actions toolbar into a reserved lane so it no longer overlays the last line of content. - Composite the agent config header tint over an opaque surface so scrolled content can't bleed through the sticky header. - Drop the rich composer's duplicate toolbar divider and re-inset it so it no longer touches the panel edges; use antd Button for the session-tag controls (raw <button> kept native chrome under disabled Tailwind preflight).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR replaces per-session boolean streaming state with a ChangesSession Run Status and Tag Bar
Sequence Diagram(s)sequenceDiagram
participant AgentConversation
participant setSessionStatusAtom
participant sessionStatusAtomFamily
participant SessionTagBar
AgentConversation->>setSessionStatusAtom: publish error/awaiting/running/idle
setSessionStatusAtom->>sessionStatusAtomFamily: update per-session status record
sessionStatusAtomFamily->>SessionTagBar: status value drives status dot
AgentConversation->>setSessionStatusAtom: reset to idle on unmount
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e440e34c-c6ba-4ec8-acb7-2433498cc9cb
📒 Files selected for processing (7)
web/oss/src/components/AgentChatSlice/AgentChatPanel.tsxweb/oss/src/components/AgentChatSlice/components/AgentMessage.tsxweb/oss/src/components/AgentChatSlice/components/SessionTabLabel.tsxweb/oss/src/components/AgentChatSlice/components/SessionTagBar.tsxweb/oss/src/components/AgentChatSlice/state/sessions.tsweb/oss/src/components/Playground/Components/PlaygroundVariantConfig/assets/PlaygroundVariantConfigHeader.tsxweb/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx
| onKeyDown={(e) => { | ||
| if (e.key === "Enter" || e.key === " ") { | ||
| e.preventDefault() | ||
| onSelect() | ||
| } | ||
| }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Don't let rename keystrokes bubble into tab selection.
When SessionTabLabel swaps in its Input, pressing Space/Enter still bubbles here. That makes space impossible to type into a session name and re-triggers tab selection while renaming.
Suggested fix
onKeyDown={(e) => {
+ const target = e.target as HTMLElement | null
+ if (target?.closest("input, textarea, [contenteditable='true']")) return
if (e.key === "Enter" || e.key === " ") {
e.preventDefault()
onSelect()
}
}}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onKeyDown={(e) => { | |
| if (e.key === "Enter" || e.key === " ") { | |
| e.preventDefault() | |
| onSelect() | |
| } | |
| }} | |
| onKeyDown={(e) => { | |
| const target = e.target as HTMLElement | null | |
| if (target?.closest("input, textarea, [contenteditable='true']")) return | |
| if (e.key === "Enter" || e.key === " ") { | |
| e.preventDefault() | |
| onSelect() | |
| } | |
| }} |
| /** Set a session's run state (pass "idle" to clear, e.g. on unmount). */ | ||
| export const setSessionStatusAtom = atom( | ||
| null, | ||
| (get, set, {id, status}: {id: string; status: SessionRunStatus}) => { | ||
| const cur = get(sessionStatusByIdAtom) | ||
| if (cur[id] === status) return | ||
| set(sessionStatusByIdAtom, {...cur, [id]: status}) | ||
| }, |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
Actually clear "idle" entries from the backing map.
This setter is documented as the clear path, but it still writes {[id]: "idle"} into sessionStatusByIdAtom. Since sessionStatusAtomFamily already defaults missing keys to "idle", closed sessions will accumulate in-memory for no functional gain.
Suggested fix
export const setSessionStatusAtom = atom(
null,
(get, set, {id, status}: {id: string; status: SessionRunStatus}) => {
const cur = get(sessionStatusByIdAtom)
if (cur[id] === status) return
- set(sessionStatusByIdAtom, {...cur, [id]: status})
+ if (status === "idle") {
+ const {[id]: _removed, ...rest} = cur
+ set(sessionStatusByIdAtom, rest)
+ return
+ }
+ set(sessionStatusByIdAtom, {...cur, [id]: status})
},
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** Set a session's run state (pass "idle" to clear, e.g. on unmount). */ | |
| export const setSessionStatusAtom = atom( | |
| null, | |
| (get, set, {id, status}: {id: string; status: SessionRunStatus}) => { | |
| const cur = get(sessionStatusByIdAtom) | |
| if (cur[id] === status) return | |
| set(sessionStatusByIdAtom, {...cur, [id]: status}) | |
| }, | |
| /** Set a session's run state (pass "idle" to clear, e.g. on unmount). */ | |
| export const setSessionStatusAtom = atom( | |
| null, | |
| (get, set, {id, status}: {id: string; status: SessionRunStatus}) => { | |
| const cur = get(sessionStatusByIdAtom) | |
| if (cur[id] === status) return | |
| if (status === "idle") { | |
| const {[id]: _removed, ...rest} = cur | |
| set(sessionStatusByIdAtom, rest) | |
| return | |
| } | |
| set(sessionStatusByIdAtom, {...cur, [id]: status}) | |
| }, | |
| ) |
| // Give it a subtly tinted surface (vs the plain content): an opaque container base | ||
| // (background-color) with the translucent fill layered on top (background-image), so | ||
| // this sticky header stays opaque and scrolled content can't bleed through it. | ||
| isAgent && !embedded | ||
| ? "bg-[var(--ant-color-fill-tertiary)]" | ||
| ? "bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ant-color-fill-tertiary),var(--ant-color-fill-tertiary))]" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use a theme surface token for the sticky header base.
bg-[var(--ag-c-FFFFFF)] hardcodes a light background on the non-embedded sticky path, so dark mode will render a white header strip instead of an opaque themed surface. Keep the gradient overlay, but swap the base layer to a semantic background token.
Proposed fix
- ? "bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ant-color-fill-tertiary),var(--ant-color-fill-tertiary))]"
+ ? "bg-[var(--ant-color-bg-container)] bg-[image:linear-gradient(var(--ant-color-fill-tertiary),var(--ant-color-fill-tertiary))]"
: "bg-[var(--ag-c-FFFFFF)]"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Give it a subtly tinted surface (vs the plain content): an opaque container base | |
| // (background-color) with the translucent fill layered on top (background-image), so | |
| // this sticky header stays opaque and scrolled content can't bleed through it. | |
| isAgent && !embedded | |
| ? "bg-[var(--ant-color-fill-tertiary)]" | |
| ? "bg-[var(--ag-c-FFFFFF)] bg-[image:linear-gradient(var(--ant-color-fill-tertiary),var(--ant-color-fill-tertiary))]" | |
| // Give it a subtly tinted surface (vs the plain content): an opaque container base | |
| // (background-color) with the translucent fill layered on top (background-image), so | |
| // this sticky header stays opaque and scrolled content can't bleed through it. | |
| isAgent && !embedded | |
| ? "bg-[var(--ant-color-bg-container)] bg-[image:linear-gradient(var(--ant-color-fill-tertiary),var(--ant-color-fill-tertiary))]" | |
| : "bg-[var(--ag-c-FFFFFF)]" |
…tatus map) - Rename input now stops its own keydown so Space is typable and Enter doesn't also switch tabs (kept at the editing surface, not the tab handler). - Reveal the session close button on keyboard focus (group-focus-within), not just hover. - setSessionStatusAtom stores idle as absence: passing "idle" deletes the entry so closed sessions don't accumulate in the status map.
|
@coderabbitai review |
✅ Action performedReview finished.
|
What
UI pass on the agent playground (config panel, session tabs, and the chat composer).
Session bar → tags. The editable-card session tabs are replaced with a tag/chip bar (
SessionTagBar), rendered throughTabsrenderTabBarso the panes — and each session's liveuseChatstream / HITL approval state — keep antd's mount semantics. Each chip has a run-status dot, a truncated label (double-click to rename), and a hover close. The bar is a fixed 48px row with a bottom border so its bottom edge lines up with the config panel header on the left.One source of truth for session status.
big-agentshad a per-session streaming boolean and this branch added a richer run-state; they're unified intosessionStatusAtomFamily(idle | running | awaiting | error). The Session Inspector's "is streaming" signal now derives from it instead of a competing flag.Fixes
sticky; its tint was a translucent fill, so scrolled content showed through it. The tint is now composited over an opaque surface (theme-aware, light + dark).border-ton top of the container border, reading as a doubled bottom edge. Removed the redundant divider.<button>s, which keep native browser chrome under this repo's disabled Tailwind preflight (a stray gray box). Switched to antdButton.Why
These are all polish/regression fixes on the agent playground surface — alignment, dark-mode correctness, and removing visual artifacts (overlapping toolbar, bled-through header, doubled borders, native button chrome).
Notes
pnpm turbo lint(pre-commit) +tsconweb/oss(no new errors; the agent-touched files are clean). Not yet verified live in the running app — the local dev stack was on a different branch during this work.