feat(frontend): agent playground Build/Chat modes#5008
Conversation
Add a two-mode agent playground driven by a persistent Build/Chat header switch, with animated transitions between the 2-panel edit view and a full-screen chat. - Header Build/Chat segmented switch (agent single-view) as the single mode control; remove the splitter's own collapse/expand handles - Full-screen Chat mode: config pane collapses and a session-history side rail slides in (search, status dots, inline rename, inspect, animated add/remove); the now-empty chat top bar collapses away - Animate Build<->Chat: config flex-basis, rail width and top-bar height ease together, armed only around a toggle so drag/window-resize stay 1:1 - Fix agent config split sizing so it mounts at its max width and no longer jumps on the first drag - Center the chat body in a max-width reading column so wide panels don't sprawl - Surface per-message run metrics (tokens/cost, latency when traced) even on the no-trace path - Show per-message timestamps (relative, self-refreshing) sourced from the trace's real start time, falling back to a client first-seen stamp
|
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 adds a full-screen maximize mode for the agent chat panel, with a new session rail, trace-derived message timestamps, and coordinated playground splitter/header behavior driven by a shared maximize atom. ChangesAgent chat maximize mode, session rail, and message timestamps
Estimated code review effort: 3 (Moderate) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant PlaygroundHeader
participant chatPanelMaximizedAtom
participant AgentChatPanel
participant SessionRail
participant MainLayout
User->>PlaygroundHeader: Click "Chat" segmented control
PlaygroundHeader->>chatPanelMaximizedAtom: setChatMaximized(true)
chatPanelMaximizedAtom-->>AgentChatPanel: chatMaximized = true
AgentChatPanel->>SessionRail: collapse width to 0, set inert
AgentChatPanel->>AgentChatPanel: collapse SessionTagBar, hide extras
chatPanelMaximizedAtom-->>MainLayout: chatMaximized = true
MainLayout->>MainLayout: compute configCollapsed, animateSplit
MainLayout->>MainLayout: apply playground-splitter-animated class
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 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
web/oss/src/styles/globals.css (1)
157-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSplitter transitions don't respect
prefers-reduced-motion.The
flex-basis/opacity transitions added here have no reduced-motion guard, whereasSessionRailRowin this same cohort uses Tailwind'smotion-safe:variant for its animations. Consider wrapping these in@media (prefers-reduced-motion: no-preference)for consistency.♿ Proposed fix
+@media (prefers-reduced-motion: no-preference) { .playground-splitter-animated .ant-splitter-panel { transition: flex-basis 240ms cubic-bezier(0.4, 0, 0.2, 1); } +} /* Full-screen chat: the config panel is 0-width and driven by the header Build/Chat switch, so fade the (now inert) split bar out rather than snapping it away. */ +@media (prefers-reduced-motion: no-preference) { .playground-splitter .ant-splitter-bar { transition: opacity 200ms ease; } +}web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx (1)
204-221: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueRedundant duplicate subscription for assistant messages.
For an assistant message,
traceIdis truthy so bothownSummary(Line 216) andtimeSummary(Line 220) resolve totraceDataSummaryAtomFamily(traceId)— the same atom read twice via two separateuseAtomValuecalls. Consider derivingtraceErrorfromtimeSummary(or vice versa) whentraceIdis set, and only reading the second atom for the user-turn (turnTraceId) case.♻️ Possible consolidation
- const ownSummary = useAtomValue(traceDataSummaryAtomFamily(traceId ?? null)) - const traceError = ownSummary.error - // Timestamp uses the run's real start: own trace for an assistant turn, the paired turn trace for - // a user turn (shares the same cached atom, so no extra fetch). Falls back to the client stamp. - const timeSummary = useAtomValue(traceDataSummaryAtomFamily((traceId || turnTraceId) ?? null)) + // Timestamp uses the run's real start: own trace for an assistant turn, the paired turn trace for + // a user turn (shares the same cached atom, so no extra fetch). Falls back to the client stamp. + const timeSummary = useAtomValue(traceDataSummaryAtomFamily((traceId || turnTraceId) ?? null)) + // For an assistant turn, timeSummary already reads the own trace — reuse it instead of a second subscription. + const ownSummary = traceId ? timeSummary : useAtomValue(traceDataSummaryAtomFamily(null)) + const traceError = ownSummary.error
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 464f952c-24e9-4fd4-9a17-7ec04d65c867
📒 Files selected for processing (10)
web/oss/src/components/AgentChatSlice/AgentChatPanel.tsxweb/oss/src/components/AgentChatSlice/components/AgentMessage.tsxweb/oss/src/components/AgentChatSlice/components/SessionHistoryMenu.tsxweb/oss/src/components/AgentChatSlice/components/SessionRail.tsxweb/oss/src/components/AgentChatSlice/components/SessionTagBar.tsxweb/oss/src/components/AgentChatSlice/state/panelLayout.tsweb/oss/src/components/AgentChatSlice/state/sessions.tsweb/oss/src/components/Playground/Components/MainLayout/index.tsxweb/oss/src/components/Playground/Components/PlaygroundHeader/index.tsxweb/oss/src/styles/globals.css
| /** Per-message first-seen timestamp (ms), keyed by message id. `UIMessage`s carry no time and user | ||
| * turns get no server metadata, so we stamp it locally when a message first appears; persisted so a | ||
| * reloaded session keeps its times. */ | ||
| export const messageCreatedAtMapAtom = atomWithStorage<Record<string, number>>( | ||
| "agenta:agent-message-created-at", | ||
| {}, | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether session/message deletion paths prune messageCreatedAtMapAtom.
rg -n "messageCreatedAtMapAtom|deleteSessionAtomFamily|persistSessionMessagesAtom" web/oss/src/components/AgentChatSlice/state/sessions.ts -A5 -B5Repository: Agenta-AI/agenta
Length of output: 2273
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the full session state file around delete/persist/stamp logic.
sed -n '1,360p' web/oss/src/components/AgentChatSlice/state/sessions.ts
# Look for any cleanup/reset of the persisted timestamp map or related storage atoms.
rg -n "agenta:agent-message-created-at|messageCreatedAtMapAtom|removeItem\\(|RESET|reset.*Atom|clear.*Atom|deleteSessionAtomFamily|persistSessionMessagesAtom" web/oss/src/components/AgentChatSlice -SRepository: Agenta-AI/agenta
Length of output: 17622
Prune messageCreatedAtMapAtom when deleting a session web/oss/src/components/AgentChatSlice/state/sessions.ts:202-270 — deleteSessionAtomFamily removes the session’s messages, but the per-message timestamp map keeps those ids forever. Clearing the matching entries here keeps agenta:agent-message-created-at bounded and avoids eventual localStorage quota errors.
- SessionRail: track pending delete timers and clear them on unmount so a mid-animation unmount can't fire deleteSession against a stale scope - Message timestamps: keep the first-seen fallback map in memory instead of localStorage (the trace start time is authoritative); removes the unbounded, lifecycle-unmanaged persisted store rather than pruning it per delete - Respect prefers-reduced-motion for all Build/Chat transitions: gate the splitter flex-basis/bar-fade in globals.css behind a media query and add motion-safe to the rail-width and top-bar-height transitions - AgentMessage: read the paired-turn trace summary only for user turns, so an assistant turn no longer subscribes to the same trace atom twice
Context
The agent playground put the config panel and the chat side by side, with only a small splitter handle to hide one or the other. There was no clear way to focus on the conversation, no home for browsing past sessions, and mode changes snapped instead of animating. Messages also showed no run data and no timestamps.
This adds two explicit modes and fills in the missing chat metadata.
Changes
Build / Chat modes. A persistent segmented switch in the playground header (agent single view) toggles between:
The switch is now the single mode control. The splitter's own collapse/expand handles are removed so there is only one way to change modes.
Session rail (Chat mode). A vertical list of the full session history (the same data the history popover uses), with search, per-session status dots, inline rename, inspect on the active row, and permanent delete. Rows animate in on add and collapse out on remove.
Animated transitions. Switching Build to Chat eases the config pane width, the rail width, and the otherwise-empty chat top-bar height together over 240ms. The transition is armed only around a toggle, so dragging the splitter and resizing the window stay 1:1 with the cursor.
Split sizing fix. The agent config pane used a px default against a percentage runs default, so it mounted wider than its max and snapped smaller on the first drag. It now mounts at its configured width and resizes back cleanly.
Chat body width. Long turns are capped to a centered reading column, so a wide (maximized) panel no longer stretches bubbles and text edge to edge.
Per-message metadata (in the hover toolbar):
Notes
What to QA