Skip to content

Polish chat interfaces and unify reasoning UI#39

Merged
batmn-dev merged 5 commits intomainfrom
API-Andres-Polishes-Interfaces
Feb 20, 2026
Merged

Polish chat interfaces and unify reasoning UI#39
batmn-dev merged 5 commits intomainfrom
API-Andres-Polishes-Interfaces

Conversation

@batmn-dev
Copy link
Owner

@batmn-dev batmn-dev commented Feb 20, 2026

Summary

  • Unifies reasoning/thinking presentation across chat surfaces with a new reasoning phase flow and updated reasoning UI components.
  • Hardens streaming and provider-reasoning behavior in the chat route, including safer handling of empty reasoning output.
  • Applies broad UI polish updates (explicit icon sizing and sidebar interaction fixes) and adds supporting docs/rules for streaming UI and mutable external state patterns.

Test plan

  • Run bun run lint
  • Run bun run typecheck
  • Run bun run test
  • Verify chat streaming and reasoning display on at least one reasoning-capable model
  • Verify sidebar rail interactions and icon sizing in updated chat/settings controls

Made with Cursor


Summary by cubic

Unifies reasoning/thinking across chat surfaces with a shared, phase-aware UI and server-side timing, improving streaming clarity and reliability. Also enforces explicit icon sizing and polishes sidebar interactions, with new docs and rules to prevent common streaming and state bugs.

  • New Features

    • Introduced unified Reasoning primitives (Reasoning, ReasoningContent, ReasoningLabel) with phase-aware labels, shimmer, chevron, and CSS grid collapse.
    • Added useReasoningPhase to derive “thinking → complete” from message parts and persisted timing.
    • Tracked reasoning duration in app/api/chat/route.ts via onChunk/onFinish, persisted in messageMetadata and surfaced in the UI.
    • Removed the old app/components/chat/reasoning.ts and adopted the shared component.
    • Added research docs and skills (streaming UI lifecycle, provider reasoning config, history adaptation), troubleshooting guides, and Cursor rules for mutable external state and icon sizing.
  • Bug Fixes

    • Hardened streaming: treat empty reasoning as non-visible, bypass memoization for the last streaming message, avoid phase memoization by parts reference (AI SDK mutates parts), and guard loading-dot suppression against non-string reasoning text.
    • Prevented sidebar rail toggles while a dialog is open.
    • Enforced explicit HugeiconsIcon sizing with Tailwind size-* classes to avoid 16px overrides; applied across buttons, menus, and settings.
    • Polished UI details, including user list item rendering and consistent menu item behavior.

Written for commit d70ad3e. Summary will update on new commits.

batmn-dev and others added 4 commits February 19, 2026 17:44
…ble state rule

- Update AI SDK v6 skill and React 19 lint fixes workflow
- Polish chat components (chat, conversation, message-assistant, message, reasoning)
- Update chat-actions-menu, sidebar-project-menu, project-view
- Update dropdown-menu component
- Add streaming-ui-rendering troubleshooting doc
- Add 080-mutable-external-state cursor rule

Co-authored-by: Cursor <cursoragent@cursor.com>
Add className="size-*" alongside HugeiconsIcon size prop across all
components to prevent layout-shift from intrinsic SVG sizing. Remove
ThinkingBar stop button (API preserved). Fix user message list rendering.
Add icon-sizing rule and research docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
… docs

- Replace per-component reasoning with shared Reasoning/ReasoningContent/ReasoningLabel
  primitives from components/ui/reasoning.tsx using CSS grid collapse instead of
  ResizeObserver max-height
- Add useReasoningPhase hook for status-driven thinking/done label transitions
- Track reasoning duration in route.ts (onChunk + onFinish) and send via messageMetadata
- Delete app/components/chat/reasoning.tsx in favor of the unified component
- Expand CLAUDE.md quality enforcement with implementation decision framework and
  research-first workflow; add Implementation Philosophy section to AGENTS.md
- Add new skills: history-adaptation-system, provider-reasoning-config, streaming-ui-lifecycle
- Update plan.md: renumber items, add SDK feature evaluation ticket (#28),
  uncheck model persistence for re-verification

Co-authored-by: Cursor <cursoragent@cursor.com>
Treat empty reasoning content as non-visible so loading and reasoning UI states transition correctly, and avoid sidebar rail toggles while a dialog is open.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link

vercel bot commented Feb 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
not-a-wrapper Ready Ready Preview, Comment Feb 20, 2026 0:45am

@greptile-apps
Copy link

greptile-apps bot commented Feb 20, 2026

Greptile Summary

This PR unifies reasoning/thinking presentation across chat surfaces with a new phase-based flow that tracks reasoning duration both client-side and server-side. The changes harden streaming behavior by safely handling empty reasoning output (preventing frozen "Thinking" indicators) and apply comprehensive icon sizing fixes across 50+ components to prevent Shadcn buttonVariants CSS overrides.

Key improvements:

  • New useReasoningPhase hook consolidates reasoning state management with timer, phase tracking, and graceful handling of empty reasoning
  • Server-side reasoning timing in chat route persists reasoningDurationMs via messageMetadata for historical display
  • Replaced scrollHeight-based animation with CSS grid 0fr/1fr pattern in reasoning component (fixes content cutoff bugs)
  • Added dialog detection to sidebar rail click handler to prevent misclicks when modals are open
  • Comprehensive documentation via new cursor rules (mutable external state, icon sizing) and troubleshooting guides

Documentation additions:

  • Four new skill guides (AI SDK v6, history adaptation, provider reasoning config, streaming UI lifecycle)
  • Two new troubleshooting docs (icon sizing in buttons, streaming UI rendering)
  • Quality enforcement guidelines added to CLAUDE.md

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes follow established patterns, include comprehensive documentation, fix real bugs (empty reasoning handling, CSS animation race conditions, sidebar misclicks), and apply consistent polish (icon sizing) across the entire codebase. The reasoning timing implementation correctly handles edge cases (reasoning-only responses, errors). No logic errors, security issues, or breaking changes detected.
  • No files require special attention

Important Files Changed

Filename Overview
app/api/chat/route.ts Adds reasoning timing tracking via reasoningStartMs and reasoningDurationMs, properly freezes duration when text-delta arrives or on finish, sends metadata to client
app/components/chat/use-reasoning-phase.ts New hook that unifies reasoning phase logic with client-side timer and server-persisted duration support, handles empty reasoning gracefully
components/ui/reasoning.tsx Adds ReasoningLabel with shimmer effect, duration display, and chevron; replaces scrollHeight animation with CSS grid 0fr/1fr for content expansion
app/components/chat/message-assistant.tsx Replaces old reasoning component with unified reasoning UI using useReasoningPhase hook, adds metadata support, updates streaming indicator to caret variant
app/components/chat/use-loading-state.ts Removes showThinking logic, simplifies hasVisibleReasoning check to only suppress generating dots when reasoning has visible text
.cursor/rules/080-mutable-external-state.mdc New rule documenting mutable external state patterns for React.memo comparators with AI SDK streaming
.cursor/rules/090-icon-sizing.mdc New rule enforcing size-* Tailwind classes on HugeiconsIcon to prevent Shadcn buttonVariants override issues
app/components/layout/sidebar/app-sidebar.tsx Adds dialog check to prevent sidebar rail misclicks, applies icon sizing fixes, replaces search click handler with HistoryTrigger

Sequence Diagram

sequenceDiagram
    participant Client as Chat UI
    participant Route as /api/chat
    participant SDK as AI SDK
    participant Hook as useReasoningPhase
    participant UI as Reasoning Component

    Client->>Route: POST message
    Route->>SDK: streamText()
    
    Note over Route: reasoningStartMs = null
    
    SDK-->>Route: reasoning-delta chunk
    Route->>Route: Set reasoningStartMs = Date.now()
    Route-->>Client: Stream reasoning chunk
    
    Client->>Hook: parts updated (reasoning streaming)
    Hook->>Hook: phase = "thinking", start timer
    Hook-->>UI: isReasoningStreaming = true
    UI->>UI: Show shimmer "Thinking" + live duration
    
    SDK-->>Route: text-delta chunk (reasoning done)
    Route->>Route: reasoningDurationMs = Date.now() - reasoningStartMs
    Route-->>Client: Stream text chunk
    
    Client->>Hook: parts updated (text started)
    Hook->>Hook: phase = "complete", freeze timer
    Hook-->>UI: isReasoningStreaming = false
    UI->>UI: Show "Thought for Xs" + collapsible content
    
    SDK-->>Route: finish
    Route->>Route: Freeze reasoningDurationMs if not set
    Route-->>Client: messageMetadata with reasoningDurationMs
    
    Note over Client,UI: Historical messages use persistedDurationMs
Loading

Last reviewed commit: 188b361

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 67 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/components/chat/use-reasoning-phase.ts">

<violation number="1" location="app/components/chat/use-reasoning-phase.ts:28">
P2: `useMemo` depends on a mutable `parts` reference, which can prevent reasoning/phase updates during streaming when parts are mutated in place. Compute this derivation each render or key it off a stable snapshot so streaming updates aren’t memoized away.</violation>

<violation number="2" location="app/components/chat/use-reasoning-phase.ts:119">
P2: `persistedDurationMs` is never used for the last message when it’s already complete, so last historical messages lose their duration. Fall back to `persistedDurationMs` when the live timer is 0.</violation>
</file>

<file name="app/components/chat/use-loading-state.ts">

<violation number="1" location="app/components/chat/use-loading-state.ts:35">
P2: Guard `part.text` before calling `trim()` to avoid runtime errors when reasoning parts have no text yet during streaming.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Avoid memoizing reasoning phase by parts reference because AI SDK mutates parts in place, and guard loading-dot suppression against non-string reasoning text.

Co-authored-by: Cursor <cursoragent@cursor.com>
@batmn-dev batmn-dev merged commit 16876ae into main Feb 20, 2026
6 checks passed
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