Skip to content

Align Agent Mode chat with normal chat - #614

Merged
AnthonyRonning merged 1 commit into
masterfrom
codex-agent-mode-chat-style-maple
Jul 11, 2026
Merged

Align Agent Mode chat with normal chat#614
AnthonyRonning merged 1 commit into
masterfrom
codex-agent-mode-chat-style-maple

Conversation

@AnthonyRonning

@AnthonyRonning AnthonyRonning commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • extract shared user-turn, assistant-turn, conversation-header, and composer primitives from normal chat and consume them from both chat modes
  • align Agent Mode transcript spacing, user bubbles, Maple assistant identity, thinking rows, titles, composer, and footer with the normal chat interface
  • group Agent timeline activity into stable assistant turns and distinguish live Thinking from completed Thought
  • restyle tool and system rows with the normal chat visual language while preserving expandable input/output details
  • retain Agent-specific permission controls with a restrained approval card and unchanged native decision handling

Why

Agent Mode had developed a parallel orange-and-boxed chat presentation that no longer matched Maple's normal chat experience. Sharing the same rendering primitives makes normal chat the actual golden implementation and reduces future visual drift while keeping Agent-only tools and approvals functional.

Validation

  • nix develop -c just format
  • nix develop -c bun test — 72 passed, 0 failed
  • nix develop -c just lint — 0 errors; 12 pre-existing warnings
  • nix develop -c just build
  • managed-workspace smoke with OpenSecret, billing, Postgres, Continuum, and Tinfoil
  • Pro fixture account in the native macOS app
  • light- and dark-mode comparison of normal chat and Agent Mode
  • live Thinking to Thought transition, tool status rows, approval denial, and run cancellation
  • confirmed denied write did not create the requested file

Packaging note

The debug macOS .app bundled and launched successfully. The subsequent updater-archive signing step reports the expected missing local TAURI_SIGNING_PRIVATE_KEY after the runnable app bundle has already been produced.

Summary by CodeRabbit

  • New Features

    • Added consistent conversation headers, user and assistant message layouts, and composer styling across chat views.
    • Added clearer grouping of messages, thinking steps, tool activity, permissions, and system updates in Agent Mode.
    • Added active thinking indicators and improved handling of ongoing runs.
    • Added an AI disclaimer and refreshed empty-state messaging, including privacy and encryption details.
  • Improvements

    • Refined timeline visuals, status indicators, spacing, scrolling, and composer focus behavior.
    • Thinking messages can now display concise labels without duration details.
  • Tests

    • Expanded coverage for timeline grouping and active thinking behavior.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Agent Mode and UnifiedChat now share reusable chat layouts and composer styling. Agent timeline items are grouped into turns with active-thinking detection, while permission, system, tool, empty-state, and thinking presentations are updated and tested.

Changes

Chat and agent timeline rendering

Layer / File(s) Summary
Shared chat rendering primitives
frontend/src/components/chat/ChatTurn.tsx
Adds reusable avatar, user-turn, assistant-turn, desktop-header, textarea-style, and composer-surface components.
Agent timeline turn modeling
frontend/src/services/agentTimeline.ts, frontend/src/components/markdown.tsx, frontend/src/services/agentTimeline.test.ts
Adds grouped timeline turns and active-thinking selection, supports shorter thinking labels, and tests grouping, activity, and stable assistant turn IDs.
Unified chat component integration
frontend/src/components/UnifiedChat.tsx
Replaces inline message, header, loading, and composer markup with shared chat components while retaining copy and TTS actions.
Agent Mode timeline and layout integration
frontend/src/components/AgentMode.tsx
Integrates grouped timeline rendering and active status handling, and updates headers, composer layout, empty state, permission rows, system rows, and tool visuals.

Estimated code review effort: 4 (Complex) | ~60 minutes

Poem

I’m a rabbit with a tidy chat,
Grouping thoughts beneath my hat.
Turns now hop in rows so neat,
Composers make the layout sweet.
Thinking fades, then tools appear—
A polished burrow, bright and clear!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: aligning Agent Mode’s chat UI with the normal chat interface.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex-agent-mode-chat-style-maple

Comment @coderabbitai help to get the list of available commands.

@AnthonyRonning
AnthonyRonning marked this pull request as ready for review July 11, 2026 07:52

@devin-ai-integration devin-ai-integration 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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/src/components/AgentMode.tsx (2)

2647-2667: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Custom status <span> replaces the shadcn Badge component.

PermissionRow previously used Badge; this now renders a manually styled <span> for the permission status text. Per path instructions, existing shadcn/ui components (src/components/ui/) should be used instead of hand-rolled UI elements. If Badge couldn't support aria-live/role="status" directly, consider wrapping Badge with those attributes rather than dropping it entirely.

As per path instructions, "Use existing shadcn/ui components from src/components/ui/ instead of creating custom UI components" for frontend/src/components/**/*.tsx.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/AgentMode.tsx` around lines 2647 - 2667, Update the
permission status element in PermissionRow to use the existing shadcn Badge
component instead of the manually styled span. Preserve the current status text,
styling intent, role="status", and aria-live="polite" behavior by applying
supported attributes to Badge or wrapping it when necessary.

Source: Path instructions


2509-2621: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider memoizing timeline grouping.

coalesceAdjacentThinkingItems, .filter(isRenderableTimelineItem), groupAgentTimelineItems, and activeAgentThinkingItemId all run unmemoized on every render of AgentTimeline. Since timelineItems updates on every streamed event, this recomputes and reallocates arrays each time. For long-running agent sessions this could add unnecessary work on the hot render path.

♻️ Suggested refactor
 function AgentTimeline({
   items,
   isRunActive,
   onPermissionDecision
 }: {
   items: AgentTimelineItem[];
   isRunActive: boolean;
   onPermissionDecision: (item: AgentTimelineItem, decision: AgentPermissionDecision) => void;
 }) {
-  const visibleItems = coalesceAdjacentThinkingItems(items).filter(isRenderableTimelineItem);
-  const turns = groupAgentTimelineItems(visibleItems);
-  const activeThinkingItemId = activeAgentThinkingItemId(visibleItems, isRunActive);
+  const visibleItems = useMemo(
+    () => coalesceAdjacentThinkingItems(items).filter(isRenderableTimelineItem),
+    [items]
+  );
+  const turns = useMemo(() => groupAgentTimelineItems(visibleItems), [visibleItems]);
+  const activeThinkingItemId = useMemo(
+    () => activeAgentThinkingItemId(visibleItems, isRunActive),
+    [visibleItems, isRunActive]
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/AgentMode.tsx` around lines 2509 - 2621, Memoize the
derived timeline data in AgentTimeline: wrap coalesceAdjacentThinkingItems, the
isRenderableTimelineItem filter, groupAgentTimelineItems, and
activeAgentThinkingItemId in a useMemo keyed by items and isRunActive. Preserve
the existing rendering behavior while avoiding recomputation when those inputs
are unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/chat/ChatTurn.tsx`:
- Around line 118-119: Reformat the CHAT_COMPOSER_TEXTAREA_CLASS declaration and
the similarly affected shared class string around the referenced lines so every
source line is at most 100 characters. Split each literal across adjacent
strings while preserving the exact generated class names and styling.

---

Nitpick comments:
In `@frontend/src/components/AgentMode.tsx`:
- Around line 2647-2667: Update the permission status element in PermissionRow
to use the existing shadcn Badge component instead of the manually styled span.
Preserve the current status text, styling intent, role="status", and
aria-live="polite" behavior by applying supported attributes to Badge or
wrapping it when necessary.
- Around line 2509-2621: Memoize the derived timeline data in AgentTimeline:
wrap coalesceAdjacentThinkingItems, the isRenderableTimelineItem filter,
groupAgentTimelineItems, and activeAgentThinkingItemId in a useMemo keyed by
items and isRunActive. Preserve the existing rendering behavior while avoiding
recomputation when those inputs are unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 41e5ba8b-054a-46e3-83ee-ffb7b01a843e

📥 Commits

Reviewing files that changed from the base of the PR and between e892ddb and 1bb5018.

📒 Files selected for processing (6)
  • frontend/src/components/AgentMode.tsx
  • frontend/src/components/UnifiedChat.tsx
  • frontend/src/components/chat/ChatTurn.tsx
  • frontend/src/components/markdown.tsx
  • frontend/src/services/agentTimeline.test.ts
  • frontend/src/services/agentTimeline.ts

Comment on lines +118 to +119
export const CHAT_COMPOSER_TEXTAREA_CLASS =
"w-full min-h-[52px] landscape-short:min-h-[40px] max-h-[200px] landscape-short:max-h-[100px] resize-none border-0 bg-transparent py-3.5 landscape-short:py-2 pl-4 pr-2 leading-6 focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep the shared class strings within the 100-character limit.

The declarations at Line 119 and Line 131 exceed the required limit. Split the literals across adjacent strings without changing the generated class names.

Suggested formatting
 export const CHAT_COMPOSER_TEXTAREA_CLASS =
-  "w-full min-h-[52px] landscape-short:min-h-[40px] max-h-[200px] landscape-short:max-h-[100px] resize-none border-0 bg-transparent py-3.5 landscape-short:py-2 pl-4 pr-2 leading-6 focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60";
+  "w-full min-h-[52px] landscape-short:min-h-[40px] max-h-[200px] "
+  "landscape-short:max-h-[100px] resize-none border-0 bg-transparent "
+  "py-3.5 landscape-short:py-2 pl-4 pr-2 leading-6 "
+  "focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60";

As per coding guidelines, TypeScript/React code must enforce a 100-character line limit.

Also applies to: 129-132

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/chat/ChatTurn.tsx` around lines 118 - 119, Reformat
the CHAT_COMPOSER_TEXTAREA_CLASS declaration and the similarly affected shared
class string around the referenced lines so every source line is at most 100
characters. Split each literal across adjacent strings while preserving the
exact generated class names and styling.

Source: Coding guidelines

@AnthonyRonning
AnthonyRonning merged commit 37d873d into master Jul 11, 2026
17 checks passed
@AnthonyRonning
AnthonyRonning deleted the codex-agent-mode-chat-style-maple branch July 11, 2026 08:16
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