Skip to content

feat(setbuilder): mobile chat view via Agent FAB + full-viewport overlay (#402)#452

Merged
thewrz merged 7 commits into
mainfrom
feat/issue-402
Jun 18, 2026
Merged

feat(setbuilder): mobile chat view via Agent FAB + full-viewport overlay (#402)#452
thewrz merged 7 commits into
mainfrom
feat/issue-402

Conversation

@thewrz

@thewrz thewrz commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Why

The WrzDJSet builder is a hard 3-column desktop grid (pool | timeline | chat)
with no mobile treatment, so the agent chat column is unusable on a phone. This
gives the agent a first-class mobile surface without attempting to make the
whole workspace responsive (a larger, separate effort).

What

On viewports ≤720px the desktop chat column is replaced by a floating Agent
FAB — carrying the live critique grade — that opens a full-viewport overlay
with the complete agent experience (critique card, tool-call cards, suggestion
chips, persona toggle, composer).

To avoid duplicating ~390 LOC of chat logic, ChatSidebar was refactored:

  • useAgentChat — hook holding all chat state + actions (critique/history
    load, send, persona, error handling).
  • ChatPanelBody — presentational body (messages, tool cards, critique,
    suggestion chips, composer) plus a shared PersonaToggle.
  • ChatSidebar — now a thin desktop shell wrapping ChatPanelBody.
  • MobileAgentOverlay — the FAB + overlay, also wrapping ChatPanelBody.
  • useIsMobile — hydration-guarded matchMedia(720px) hook so page.tsx
    renders either the desktop sidebar or the mobile overlay — never both,
    so only one useAgentChat mounts and fetches.

Touch targets are ≥44px and the overlay stacks single-column (consistent with
the #438 mobile-reorder work); the bumps are scoped to .agentOverlay so the
desktop composer is byte-for-byte unchanged.

Out of scope (documented known gap): the pool/timeline/curve workspace
itself is still not responsive.

Testing

  • Unit tests pass (npm test -- --run → 1272 passed)
  • Coverage gates pass (npm test -- --run --coverage → exit 0; 78.75% stmts
    / 69.89% branch / 72.75% funcs / 81.13% lines)
  • Lint clean (npm run lint) + TypeScript strict (npx tsc --noEmit)
  • Desktop ChatSidebar.test.tsx (11 tests) stays green as the
    behavior-preservation guard
  • Manual verification: load a set on a ≤720px viewport → Agent FAB shows
    grade → tap opens overlay → critique/tool cards/chips legible → send works
    → close returns to the builder; desktop sidebar unchanged
  • CI green

🤖 Co-authored by Claude Opus 4.8. Closes #402.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added mobile-optimized chat experience: users can now access the agent chat via a floating "Agent" button on smaller screens, which opens a full-viewport overlay for seamless conversations
    • Desktop users remain unaffected; the existing sidebar chat experience is unchanged
  • Documentation

    • Added implementation and design documentation for the mobile chat feature

thewrz and others added 6 commits June 17, 2026 17:48
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hydration-safe matchMedia hook (defaults desktop on first render) that
gates the mobile agent chat surface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…idebar (#402)

Split the agent-chat logic into a reusable useAgentChat hook and a
presentational ChatPanelBody (with a shared PersonaToggle) so the desktop
sidebar and the upcoming mobile overlay can share one implementation.
ChatSidebar is now a thin desktop shell; its existing test suite stays green
as the behavior-preservation guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A floating Agent FAB surfaces the critique grade and opens a full-viewport
overlay reusing useAgentChat + ChatPanelBody. History loads lazily on open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
page.tsx renders EITHER the desktop ChatSidebar OR the MobileAgentOverlay
based on useIsMobile, so only one useAgentChat mounts and fetches. CSS adds
the FAB + full-viewport overlay (>=44px touch targets, overlay-scoped so the
desktop composer stays compact) and drops the now-empty chat grid column on
narrow viewports. The pool/timeline workspace remains a documented
non-responsive gap, out of scope here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@thewrz, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 40 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f49a505-6528-46b0-b7c1-c57e705bda38

📥 Commits

Reviewing files that changed from the base of the PR and between 82552a8 and 2119717.

📒 Files selected for processing (5)
  • dashboard/app/(dj)/setbuilder/[setId]/page.tsx
  • dashboard/app/(dj)/setbuilder/components/MobileAgentOverlay.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/MobileAgentOverlay.test.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/useAgentChat.test.tsx
  • dashboard/app/(dj)/setbuilder/components/useAgentChat.ts
📝 Walkthrough

Walkthrough

Extracts all agent-chat state into a new useAgentChat hook and all rendering into a new ChatPanelBody component. Adds MobileAgentOverlay (FAB + full-viewport overlay) and useIsMobile for responsive mounting. ChatSidebar becomes a thin desktop shell; [setId]/page.tsx conditionally mounts one or the other. CSS adds FAB/overlay styles and drops the chat column from the mobile workspace grid.

Changes

Mobile Agent Chat — Issue #402

Layer / File(s) Summary
useIsMobile hook and tests
...components/useIsMobile.ts, ...components/__tests__/useIsMobile.test.tsx
New hook tracks viewport width via matchMedia, starts at false for SSR safety, and registers/cleans up a change listener. Tests cover initial match/non-match state, query string format, and change-event reactivity.
useAgentChat hook: types, state, and send
...components/useAgentChat.ts
Defines Persona, ChatEntry, AgentChatController, and formatAgentError. Two effects handle critique loading (on setId/refreshToken) and history loading (when open) with stale-response guards. Async send manages pending entries, API calls, mutation callbacks, and error state.
ChatPanelBody shared UI and tests
...components/ChatPanelBody.tsx, ...components/__tests__/ChatPanelBody.test.tsx
New file with 257 lines: flag/tone helpers, PersonaToggle, ToolCard, CritiqueCard, and the main ChatPanelBody (auto-scroll, message list, suggestion chips, Enter-to-send composer). Tests assert critique card, tool call display, suggestion chip, and Send button behaviors.
ChatSidebar refactored to thin shell
...components/ChatSidebar.tsx
Removes ~353 lines of inline state/rendering. Collapsed grade chip reads from chat.critique; expanded view delegates to PersonaToggle and ChatPanelBody via useAgentChat.
MobileAgentOverlay and integration tests
...components/MobileAgentOverlay.tsx, ...components/__tests__/MobileAgentOverlay.test.tsx
New component renders a fixed FAB showing overall_grade; tapping opens a full-viewport overlay with close control, grade, PersonaToggle, and ChatPanelBody. History loads only while open. Tests cover FAB grade, overlay open → getSetAgentHistory, message send → chatWithSetAgent, and close behavior.
Responsive page wiring and CSS
...[setId]/page.tsx, ...setbuilder.module.css
BuilderPage calls useIsMobile() and conditionally renders ChatSidebar or MobileAgentOverlay. CSS adds .agentFab/.agentOverlay selectors, overlay-scoped touch-target rules, and updates the @media (max-width: 720px) workspace grid to remove the chat column.
Design spec and plan docs
docs/superpowers/specs/..., docs/superpowers/plans/...
Adds the approved design spec (component structure, responsive strategy, test expectations) and a task-by-task implementation plan checklist.

Sequence Diagram(s)

sequenceDiagram
  participant BuilderPage
  participant useIsMobile
  participant ChatSidebar
  participant MobileAgentOverlay
  participant useAgentChat
  participant api

  BuilderPage->>useIsMobile: matchMedia("max-width: 720px")
  useIsMobile-->>BuilderPage: isMobile = true / false

  alt Desktop (isMobile = false)
    BuilderPage->>ChatSidebar: render with setId, refreshToken
    ChatSidebar->>useAgentChat: open=true, setId, refreshToken
  else Mobile (isMobile = true)
    BuilderPage->>MobileAgentOverlay: render with setId, refreshToken
    MobileAgentOverlay->>useAgentChat: open=false initially
    Note over MobileAgentOverlay: FAB shows overall_grade
    MobileAgentOverlay->>useAgentChat: open=true on FAB tap
  end

  useAgentChat->>api: critiqueSet(setId)
  api-->>useAgentChat: critique

  useAgentChat->>api: getSetAgentHistory(setId)
  api-->>useAgentChat: history entries

  useAgentChat->>api: chatWithSetAgent(setId, message) on send
  api-->>useAgentChat: user + assistant entries
  useAgentChat->>BuilderPage: onMutationApplied() if mutating tools
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • wrzonance/WrzDJ#434: Updates ChatSidebar for locked-slot agent messaging, which directly overlaps with useAgentChat's new formatAgentError locked-error branch and ChatPanelBody's locked-skip rendering extracted from that sidebar.
  • wrzonance/WrzDJ#443: Adds critique_set flag chip display in chat tool cards, which is now rendered by the new shared ChatPanelBody/ToolCard introduced in this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: introducing a mobile chat view via Agent FAB and full-viewport overlay for issue #402.
Linked Issues check ✅ Passed All four scope items from issue #402 are met: mobile chat surface (full-viewport overlay via FAB), tool-call cards rendering legibly, suggestion chips optimized for touch (≥44px targets), and critique card adapted for mobile.
Out of Scope Changes check ✅ Passed Changes align with issue #402 scope. The refactoring of ChatSidebar, extraction of useAgentChat and ChatPanelBody, and addition of MobileAgentOverlay are all necessary to support the mobile chat surface without duplication.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-402

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

@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: 6

🧹 Nitpick comments (2)
dashboard/app/(dj)/setbuilder/components/useAgentChat.ts (2)

50-54: ⚡ Quick win

Document explicit agent capabilities and limitations in this file.

The comment explains purpose, but it doesn’t explicitly enumerate limitations/behavioral boundaries (e.g., history only when open, one-send-at-a-time semantics, stale-request handling constraints). Add a concise capabilities/limitations section near the hook docs.
As per coding guidelines: “Document agent capabilities and limitations in agent implementation files” .

🤖 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 `@dashboard/app/`(dj)/setbuilder/components/useAgentChat.ts around lines 50 -
54, The useAgentChat hook's documentation comment lacks explicit enumeration of
its agent capabilities and behavioral limitations. Expand the existing comment
block (before the useAgentChat function definition) to include a concise
"Capabilities and Limitations" section that documents specific behavioral
boundaries such as history loading only when the agent is open,
one-send-at-a-time semantics for message sending, and stale-request handling
constraints to help developers understand the hook's boundaries and expected
behavior.

Source: Coding guidelines


77-171: ⚡ Quick win

Add structured logging for agent actions and state transitions.

The hook currently performs critique/history fetches and send lifecycle transitions without logging, which makes runtime diagnosis harder in production incidents.
As per coding guidelines: “Use logging for agent actions and state changes to aid debugging and monitoring” .

🤖 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 `@dashboard/app/`(dj)/setbuilder/components/useAgentChat.ts around lines 77 -
171, The hook lacks structured logging for agent actions and state transitions,
making production debugging difficult. Add logging calls to track: the start and
completion of the critiqueSet() API call and any errors in the first useEffect,
the start and completion of the getSetAgentHistory() API call in the second
useEffect, and the execution flow within the send() function including when
chatWithSetAgent() is called, when entries are updated, and when mutations are
applied. Use consistent logging levels (info for actions, error for failures) to
provide visibility into the hook's runtime behavior.

Source: Coding guidelines

🤖 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 `@dashboard/app/`(dj)/setbuilder/[setId]/page.tsx:
- Line 71: The useIsMobile hook initially returns false on mobile clients before
hydration completes, causing ChatSidebar to mount first on mobile, then switch
to MobileAgentOverlay at lines 432-450, which triggers useAgentChat twice. To
fix this, add a conditional render guard in the ChatSidebar section (around
lines 432-450) to only render ChatSidebar when isMobile is definitively false,
either by wrapping the ChatSidebar component in a condition that checks
isMobile, or by adding a skip flag to prevent the desktop chat component from
mounting until hydration confirms the actual device type. This prevents the
duplicate agent initialization during first paint.

In
`@dashboard/app/`(dj)/setbuilder/components/__tests__/MobileAgentOverlay.test.tsx:
- Around line 16-88: The test suite for MobileAgentOverlay currently lacks
coverage for the persona switching functionality (handled by PersonaToggle
component) and error scenarios from the useAgentChat hook. Add two new test
cases: one that verifies the PersonaToggle behavior when a different persona is
selected and that the appropriate API calls or state changes occur, and another
that mocks the chatWithSetAgent API to reject or return an error state, then
asserts that the error fallback UI from ChatPanelBody is rendered correctly
(such as an error message or retry affordance).

In `@dashboard/app/`(dj)/setbuilder/components/MobileAgentOverlay.tsx:
- Around line 24-47: Add logging telemetry when the agent overlay open state
changes in the MobileAgentOverlay component. Specifically, add a log statement
before or after the setOpen(true) call in the first button's onClick handler to
indicate the overlay is opening, and similarly add a log statement before or
after the setOpen(false) call in the close button's onClick handler to indicate
the overlay is closing. This will ensure agent-session state transitions are
tracked for production debugging and monitoring purposes.
- Around line 42-57: The MobileAgentOverlay component sets up proper dialog
semantics with role="dialog" and aria-modal="true", but does not manage focus
lifecycle. Add focus management to move focus into the dialog when it opens
(consider using a ref on the styles.agentOverlay container or the close button)
and restore focus to the previously focused element when the dialog closes via
setOpen(false). Use useEffect with the open state to handle focus on mount and
cleanup on unmount, ensuring keyboard users remain within the dialog context.

In `@dashboard/app/`(dj)/setbuilder/components/useAgentChat.ts:
- Around line 55-186: The useAgentChat hook contains critical functionality
without corresponding unit or integration tests. Create tests that cover the key
flows: the critique loading effect triggered by setId/refreshToken changes, the
history loading effect with stale-request guards using historyRequestIdRef, the
send function which creates pending entries and handles the API response with
mutation detection via onMutationApplied callback, error handling in both
useEffect blocks using setError and historyErrorRef, and the error normalization
through formatAgentError. Ensure tests validate that cancelled requests are
properly ignored and that pending entries are correctly replaced after
successful sends.
- Around line 132-170: The `send()` function has a race condition where the
`busy` guard check happens before `setBusy(true)` commits to state, allowing
rapid consecutive calls in the same tick to both pass the guard and trigger
duplicate API requests. Create a ref (such as `busyRef`) to synchronously track
the busy state, set `busyRef.current = true` immediately at the start of the
`send()` function right after the message validation check, and update the guard
clause to check `busyRef.current` instead of the state-based `busy` variable.
This ensures subsequent calls in the same tick will see the updated state before
proceeding.

---

Nitpick comments:
In `@dashboard/app/`(dj)/setbuilder/components/useAgentChat.ts:
- Around line 50-54: The useAgentChat hook's documentation comment lacks
explicit enumeration of its agent capabilities and behavioral limitations.
Expand the existing comment block (before the useAgentChat function definition)
to include a concise "Capabilities and Limitations" section that documents
specific behavioral boundaries such as history loading only when the agent is
open, one-send-at-a-time semantics for message sending, and stale-request
handling constraints to help developers understand the hook's boundaries and
expected behavior.
- Around line 77-171: The hook lacks structured logging for agent actions and
state transitions, making production debugging difficult. Add logging calls to
track: the start and completion of the critiqueSet() API call and any errors in
the first useEffect, the start and completion of the getSetAgentHistory() API
call in the second useEffect, and the execution flow within the send() function
including when chatWithSetAgent() is called, when entries are updated, and when
mutations are applied. Use consistent logging levels (info for actions, error
for failures) to provide visibility into the hook's runtime behavior.
🪄 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 Plus

Run ID: bae564c5-def1-409d-9546-6486a7c0e241

📥 Commits

Reviewing files that changed from the base of the PR and between fbc46f6 and 82552a8.

📒 Files selected for processing (12)
  • dashboard/app/(dj)/setbuilder/[setId]/page.tsx
  • dashboard/app/(dj)/setbuilder/components/ChatPanelBody.tsx
  • dashboard/app/(dj)/setbuilder/components/ChatSidebar.tsx
  • dashboard/app/(dj)/setbuilder/components/MobileAgentOverlay.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/ChatPanelBody.test.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/MobileAgentOverlay.test.tsx
  • dashboard/app/(dj)/setbuilder/components/__tests__/useIsMobile.test.tsx
  • dashboard/app/(dj)/setbuilder/components/useAgentChat.ts
  • dashboard/app/(dj)/setbuilder/components/useIsMobile.ts
  • dashboard/app/(dj)/setbuilder/setbuilder.module.css
  • docs/superpowers/plans/2026-06-17-issue-402-mobile-chat.md
  • docs/superpowers/specs/2026-06-17-issue-402-design.md

Comment thread dashboard/app/(dj)/setbuilder/[setId]/page.tsx
Comment thread dashboard/app/(dj)/setbuilder/components/MobileAgentOverlay.tsx
Comment thread dashboard/app/(dj)/setbuilder/components/MobileAgentOverlay.tsx
Comment thread dashboard/app/(dj)/setbuilder/components/useAgentChat.ts
Comment thread dashboard/app/(dj)/setbuilder/components/useAgentChat.ts
Address CodeRabbit review on PR #452:

- page.tsx: gate both chat surfaces behind a post-hydration `chatSurfaceReady`
  flag so a mobile client never briefly mounts the desktop sidebar (and its
  duplicate agent fetches) before `useIsMobile` resolves.
- useAgentChat: add a synchronous `sendInFlightRef` guard so two `send()` calls
  in the same tick cannot both pass the `busy` check and fire duplicate
  requests; document the hook's capabilities/limitations.
- MobileAgentOverlay: manage dialog focus lifecycle — move focus to the close
  button on open, restore it to the FAB on close, and close on Escape.
- Add useAgentChat hook tests (critique/history effects, optimistic send,
  same-tick double-send guard, mutation callback, error normalization) and
  MobileAgentOverlay tests (persona switch, error fallback, focus + Escape).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thewrz

thewrz commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator Author

CodeRabbit body nitpicks — useAgentChat.ts (addressed in 2119717):

  • L50–54 — document capabilities/limitations: Done. Expanded the hook docstring with an explicit "Capabilities & limitations" section (critique loads on mount; history only while open; one-send-at-a-time via the in-flight guard; no retry/streaming).
  • L77–171 — structured logging: Declining — same rationale as the inline logging comment on MobileAgentOverlay. This is a client React hook with no logging/telemetry framework, and the repo's coding style bans console.log in app code (enforced by lint + commit hooks). Adding client-side logging here would violate house style without aiding production diagnosis.

@thewrz thewrz merged commit 1a5610b into main Jun 18, 2026
11 checks passed
@thewrz thewrz deleted the feat/issue-402 branch June 18, 2026 01:34
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.

WrzDJSet: Mobile chat view

1 participant