Skip to content

Remove tool call grouping UI and show individual calls - #102

Merged
2witstudios merged 4 commits into
masterfrom
claude/remove-tool-grouping-ui-vgscG
Dec 20, 2025
Merged

2witstudios merged 4 commits into
masterfrom
claude/remove-tool-grouping-ui-vgscG

Conversation

@2witstudios

@2witstudios 2witstudios commented Dec 20, 2025 •

Copy link
Copy Markdown
Owner
  • Remove GroupedToolCallsRenderer and CompactGroupedToolCallsRenderer components
  • Simplify MessageRenderer and CompactMessageRenderer to render each tool individually
  • Remove tool-calls-group logic and ToolCallsGroupPart type
  • Update tool-calls/index.ts exports
  • Delete grouped-tool-calls-ui.md documentation

Each tool call now renders inline without grouping, providing a cleaner UI.

Summary by CodeRabbit

  • Refactor

    • Collapsible/grouped tool-call UI removed — tool call entries now appear individually in full and compact chat views.
    • Message rendering simplified: consecutive text parts are merged into single blocks; timestamps now display on the appropriate final part.
    • Todo list messages now reflect live task state updates (socket-driven).
  • Documentation

    • Grouped tool calls UI design document removed.

✏️ Tip: You can customize this high-level summary in your review settings.

- Remove GroupedToolCallsRenderer and CompactGroupedToolCallsRenderer components
- Simplify MessageRenderer and CompactMessageRenderer to render each tool individually
- Remove tool-calls-group logic and ToolCallsGroupPart type
- Update tool-calls/index.ts exports
- Delete grouped-tool-calls-ui.md documentation

Each tool call now renders inline without grouping, providing a cleaner UI.
@coderabbitai

coderabbitai Bot commented Dec 20, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Replaces inline message-part typing and handcrafted grouping with a shared message-types module and useGroupedParts hook, removes grouped-tool-calls UI components and docs, and updates message renderers to consume grouped parts and render tool calls individually.

Changes

Cohort / File(s) Summary
Message renderer adaptations
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx, apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
Removed local part/type declarations and manual grouping; import ConversationMessage, GroupedPart, etc., and call useGroupedParts(message.parts). Eliminated grouped tool-calls rendering paths and related in-file guards; now use isTextGroupPart / isProcessedToolPart and render tool parts individually.
New shared types & hook
apps/web/src/components/ai/shared/chat/message-types.ts, apps/web/src/components/ai/shared/chat/useGroupedParts.ts
Added message-types exports (ConversationMessage, TextPart, ToolPart/ProcessedToolPart, TextGroupPart, GroupedPart, VALID_TOOL_STATES, runtime guards) and useGroupedParts hook that skips non-renderable parts, merges consecutive text parts into text-group, and normalizes tool-* parts to ProcessedToolPart with validated state.
Removed grouped-tool-calls renderers
apps/web/src/components/ai/shared/chat/tool-calls/CompactGroupedToolCallsRenderer.tsx, apps/web/src/components/ai/shared/chat/tool-calls/GroupedToolCallsRenderer.tsx
Deleted both components and their helpers/types that implemented collapsible grouped UI, status aggregation, optimistic updates, task-specific logic, and all related rendering.
Tool-calls public exports updated
apps/web/src/components/ai/shared/chat/tool-calls/index.ts
Removed public exports for GroupedToolCallsRenderer and CompactGroupedToolCallsRenderer.
Docs removed
docs/3.0-guides-and-tools/grouped-tool-calls-ui.md
Deleted design documentation describing the grouped tool-calls UI, grouping rules, and component behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus:
    • MessageRenderer.tsx and CompactMessageRenderer.tsx integration with useGroupedParts and updated type imports.
    • Correctness of useGroupedParts grouping rules (skips, text merging, tool name/state derivation).
    • Ensuring no remaining references to removed grouped-tool-calls components or their types.
    • Type-checking across modules after swapping local types for message-types.

Possibly related PRs

Poem

🐰 I hopped through lines and sorted the parts,
Bundled text together, set each tool apart.
Types now travel outward, the hook hums along,
Gone are tangled groups — the renderers sing a new song.
✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately describes the main change: removing the tool call grouping UI and displaying individual calls instead, which aligns with the primary refactoring across multiple renderer components.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/remove-tool-grouping-ui-vgscG

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e6d7bf and 9574c59.

📒 Files selected for processing (3)
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (5 hunks)
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (5 hunks)
  • apps/web/src/components/ai/shared/chat/message-types.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
apps/web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/**/*.tsx: For document editing, register editing state using useEditingStore.getState().startEditing() and endEditing() to prevent unwanted UI refreshes
For AI streaming operations, register streaming state using useEditingStore.getState().startStreaming() and endStreaming() to prevent unwanted UI refreshes
When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
**/{components,src/**/components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for React component names and filenames

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)
📚 Learning: 2025-12-16T19:03:59.870Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Keep commits and diffs minimal and focused on specific changes

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-16T19:06:20.385Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/ui/Image.tsx:2-2
Timestamp: 2025-12-16T19:06:20.385Z
Learning: In apps/web/src/components/ai/ui/Image.tsx (TypeScript/React), the intentional use of `Experimental_GeneratedImage` from the Vercel AI SDK is accepted. This type is the correct and intended way to handle AI-generated images with base64/mediaType properties, and will be updated when the AI SDK stabilizes this API.

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
🧬 Code graph analysis (1)
apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (3)
apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1)
  • useGroupedParts (17-72)
apps/web/src/components/ai/shared/chat/message-types.ts (2)
  • isTextGroupPart (80-82)
  • isProcessedToolPart (87-89)
apps/web/src/components/ai/shared/chat/tool-calls/ToolCallRenderer.tsx (1)
  • ToolCallRenderer (71-212)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Unit Tests
🔇 Additional comments (5)
apps/web/src/components/ai/shared/chat/message-types.ts (1)

1-89: Excellent centralization of message types!

The new shared type module is well-designed and provides a clean contract for message rendering:

  • Proper TypeScript types throughout (no any usage)
  • Clear discriminated unions with runtime type guards
  • Separation between raw ToolPart (from AI SDK) and ProcessedToolPart (for rendering)
  • Centralized validation with VALID_TOOL_STATES and isValidToolState

This refactoring significantly improves type safety and maintainability across the message rendering pipeline.

apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (2)

1-14: Clean import refactoring.

The migration to centralized types and the new useGroupedParts hook simplifies the component's responsibilities. Proper separation between types and logic.


237-369: Well-executed refactoring of rendering logic.

The use of useGroupedParts and type guards (isTextGroupPart, isProcessedToolPart) provides clean type narrowing without type assertions. The timestamp logic correctly shows createdAt/editedAt only on the last text block, and tool parts are properly mapped to ToolCallRenderer.

apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (2)

1-13: Consistent import structure with MessageRenderer.

The compact renderer follows the same refactoring pattern, using centralized types and the shared useGroupedParts hook.


239-370: Rendering logic properly refactored with consistent patterns.

The compact renderer mirrors the MessageRenderer refactoring approach:

  • Uses useGroupedParts for processing
  • Applies type guards for type-safe rendering branches
  • Maintains compact UI styling while sharing core logic patterns

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

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (1)

306-311: Consider extracting the state validation logic to reduce duplication.

The validStates array and isValidState type guard are duplicated verbatim in CompactMessageRenderer.tsx. Consider extracting these to a shared utility or the ToolPart/ToolGroupPart type definition file.

Example shared utility
// In a shared types or utils file (e.g., tool-calls/types.ts)
export const VALID_TOOL_STATES = ['input-streaming', 'input-available', 'output-available', 'output-error', 'done', 'streaming'] as const;
export type ValidToolState = typeof VALID_TOOL_STATES[number];

export const isValidToolState = (value: unknown): value is ValidToolState => {
  return typeof value === 'string' && (VALID_TOOL_STATES as readonly string[]).includes(value);
};
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (1)

23-49: Significant duplication with MessageRenderer.tsx — consider consolidating shared types and logic.

The interfaces (TextPart, ToolPart, TextGroupPart, ToolGroupPart) and the groupedParts useMemo logic are nearly identical between MessageRenderer.tsx and CompactMessageRenderer.tsx. Consider:

  1. Extracting shared interfaces to a common types file
  2. Creating a shared hook (e.g., useGroupedParts) for the grouping logic

This would reduce maintenance burden and ensure consistency when the logic changes.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc9ef93 and 3eb4955.

📒 Files selected for processing (6)
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (4 hunks)
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (4 hunks)
  • apps/web/src/components/ai/shared/chat/tool-calls/CompactGroupedToolCallsRenderer.tsx (0 hunks)
  • apps/web/src/components/ai/shared/chat/tool-calls/GroupedToolCallsRenderer.tsx (0 hunks)
  • apps/web/src/components/ai/shared/chat/tool-calls/index.ts (0 hunks)
  • docs/3.0-guides-and-tools/grouped-tool-calls-ui.md (0 hunks)
💤 Files with no reviewable changes (4)
  • apps/web/src/components/ai/shared/chat/tool-calls/index.ts
  • apps/web/src/components/ai/shared/chat/tool-calls/GroupedToolCallsRenderer.tsx
  • apps/web/src/components/ai/shared/chat/tool-calls/CompactGroupedToolCallsRenderer.tsx
  • docs/3.0-guides-and-tools/grouped-tool-calls-ui.md
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
apps/web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/**/*.tsx: For document editing, register editing state using useEditingStore.getState().startEditing() and endEditing() to prevent unwanted UI refreshes
For AI streaming operations, register streaming state using useEditingStore.getState().startStreaming() and endStreaming() to prevent unwanted UI refreshes
When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
**/{components,src/**/components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for React component names and filenames

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.
📚 Learning: 2025-12-16T19:03:59.870Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Keep commits and diffs minimal and focused on specific changes

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Unit Tests
🔇 Additional comments (6)
apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (3)

3-3: LGTM!

Import updated correctly to use ToolCallRenderer instead of the removed GroupedToolCallsRenderer, aligning with the PR objective to render each tool call individually.


51-51: LGTM!

Type definition correctly simplified to only include TextGroupPart | ToolGroupPart, removing the previously existing ToolCallsGroupPart type from the union.


283-322: LGTM! Grouping logic correctly simplified for individual tool call rendering.

The changes properly implement the PR objective:

  • Text parts are still grouped together for efficient rendering
  • Tool parts are now added individually to the groups array
  • Type validation with isValidState ensures runtime safety
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (3)

3-3: LGTM!

Import updated correctly to use CompactToolCallRenderer instead of the removed CompactGroupedToolCallsRenderer.


51-51: LGTM!

Type definition correctly simplified, consistent with MessageRenderer.tsx.


285-324: LGTM! Grouping logic correctly simplified.

The implementation is consistent with MessageRenderer.tsx and correctly renders each tool call individually.

- Create message-types.ts with shared interfaces (ConversationMessage, TextPart, ToolPart, TextGroupPart, ToolGroupPart)
- Create useGroupedParts hook for shared message parts grouping logic
- Update MessageRenderer to use shared types and hook
- Update CompactMessageRenderer to use shared types and hook
- Reduces code duplication by ~200 lines

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (1)

330-372: Prefer type guards over type assertions for safer type narrowing.

The rendering logic correctly displays individual tool calls (aligning with PR objectives), but uses type assertions (as TextGroupPart at line 333 and as ToolGroupPart at line 355) which bypass TypeScript's type checking. Consider using proper type guard functions for runtime type safety.

🔎 Suggested implementation with type guards

Add type guard functions in message-types.ts:

export function isTextGroupPart(group: GroupedPart): group is TextGroupPart {
  return group.type === 'text-group';
}

export function isToolGroupPart(group: GroupedPart): group is ToolGroupPart {
  return typeof group.type === 'string' && group.type.startsWith('tool-');
}

Then update the rendering logic:

 {groupedParts.map((group, index) => {
-  if (group.type === 'text-group') {
-    // Type narrowing: we know this is a TextGroupPart
-    const textGroup = group as TextGroupPart;
+  if (isTextGroupPart(group)) {
     const isLastTextBlock = index === groupedParts.length - 1;

     return (
       <TextBlock
         key={`${message.id}-text-${index}`}
-        parts={textGroup.parts}
+        parts={group.parts}
         role={message.role as 'user' | 'assistant' | 'system'}
         messageId={message.id}
         createdAt={isLastTextBlock ? createdAt : undefined}
         editedAt={isLastTextBlock ? editedAt : undefined}
         onEdit={onEdit ? () => setIsEditing(true) : undefined}
         onDelete={onDelete ? () => setShowDeleteDialog(true) : undefined}
         onRetry={canRetry ? handleRetry : undefined}
         isEditing={isEditing}
         onSaveEdit={handleSaveEdit}
         onCancelEdit={() => setIsEditing(false)}
         isStreaming={isStreaming}
       />
     );
-  } else if (group.type.startsWith('tool-')) {
-    // Type narrowing: we know this is a ToolGroupPart
-    const toolGroup = group as ToolGroupPart;
+  } else if (isToolGroupPart(group)) {
     return (
       <div key={`${message.id}-tool-${index}`} className="mr-2 sm:mr-8">
         <ToolCallRenderer
           part={{
-            type: toolGroup.type,
-            toolName: toolGroup.toolName,
-            toolCallId: toolGroup.toolCallId,
-            input: toolGroup.input,
-            output: toolGroup.output,
-            state: toolGroup.state,
+            type: group.type,
+            toolName: group.toolName,
+            toolCallId: group.toolCallId,
+            input: group.input,
+            output: group.output,
+            state: group.state,
           }}
         />
       </div>
     );
   }
   return null;
 })}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb4955 and 0370051.

📒 Files selected for processing (4)
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (3 hunks)
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (3 hunks)
  • apps/web/src/components/ai/shared/chat/message-types.ts (1 hunks)
  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
**/{components,src/**/components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for React component names and filenames

Files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
apps/web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/**/*.tsx: For document editing, register editing state using useEditingStore.getState().startEditing() and endEditing() to prevent unwanted UI refreshes
For AI streaming operations, register streaming state using useEditingStore.getState().startStreaming() and endStreaming() to prevent unwanted UI refreshes
When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming

Files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI

Applied to files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content

Applied to files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`

Applied to files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)

Applied to files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
📚 Learning: 2025-12-16T19:03:59.870Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.

Applied to files:

  • apps/web/src/components/ai/shared/chat/useGroupedParts.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Keep commits and diffs minimal and focused on specific changes

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-16T19:06:20.385Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/ui/Image.tsx:2-2
Timestamp: 2025-12-16T19:06:20.385Z
Learning: In apps/web/src/components/ai/ui/Image.tsx (TypeScript/React), the intentional use of `Experimental_GeneratedImage` from the Vercel AI SDK is accepted. This type is the correct and intended way to handle AI-generated images with base64/mediaType properties, and will be updated when the AI SDK stabilizes this API.

Applied to files:

  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
🧬 Code graph analysis (3)
apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1)
apps/web/src/components/ai/shared/chat/message-types.ts (4)
  • GroupedPart (62-62)
  • TextPart (22-25)
  • ToolPart (30-37)
  • isValidToolState (73-75)
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (1)
apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1)
  • useGroupedParts (17-72)
apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (1)
apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1)
  • useGroupedParts (17-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Unit Tests
🔇 Additional comments (11)
apps/web/src/components/ai/shared/chat/message-types.ts (3)

11-25: LGTM! Clean type definitions.

The ConversationMessage and TextPart interfaces are well-structured and properly typed, following TypeScript best practices.


73-75: LGTM! Correct type guard implementation.

The type guard properly validates the value against VALID_TOOL_STATES with correct type narrowing. The cast to readonly string[] is necessary for the includes() method.


30-37: The ToolPart state definition is correct and complete for AI SDK 5.0 behavior. The AI SDK defines exactly four tool states: 'input-streaming', 'input-available', 'output-available', and 'output-error', which ToolPart (line 36) accurately reflects.

The 'done' and 'streaming' states in ToolGroupPart are custom application-level extensions, not states emitted by the AI SDK. This is a sound architectural pattern: ToolPart represents SDK types while ToolGroupPart represents application-processed types for rendering. The useGroupedParts hook (line 48) safely handles state validation with a fallback to 'input-available'.

Likely an incorrect or invalid review comment.

apps/web/src/components/ai/shared/chat/useGroupedParts.ts (2)

17-24: LGTM! Well-structured hook with proper memoization.

The hook correctly uses useMemo with appropriate dependency tracking and handles the empty parts case gracefully.


26-68: LGTM! Solid grouping logic with defensive programming.

The implementation correctly:

  • Groups consecutive text parts while preserving order
  • Renders tool calls individually (aligning with the PR objective)
  • Uses conservative defaults for missing or invalid properties
  • Validates tool states with the type guard

The permissive type assertion on line 45 is acceptable given the runtime validation that follows.

apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (3)

1-2: LGTM! Clean migration to centralized types and hook.

The imports correctly reference the new shared types and grouping hook, properly centralizing the message rendering logic.

Also applies to: 11-12


238-238: LGTM! Simplified grouping with the new hook.

The call to useGroupedParts(message.parts) successfully replaces the previous inline grouping logic, making the component cleaner and more maintainable.


331-373: LGTM! Correct rendering of grouped parts.

The rendering logic properly handles both text groups and individual tool parts using type narrowing. The individual tool rendering aligns with the PR objective to remove tool call grouping UI.

apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (3)

1-2: LGTM - Import changes align with PR objectives.

The updated imports correctly reflect the removal of grouped tool call rendering components and now use only ToolCallRenderer for individual tool calls.


12-13: LGTM - Centralized types and grouping logic.

Good refactoring to import types from ./message-types and use the useGroupedParts hook. This eliminates code duplication and provides a single source of truth for message part types and grouping logic.


236-236: LGTM - Hook usage simplifies grouping logic.

The useGroupedParts hook properly encapsulates the grouping logic and benefits from memoization for performance optimization.

- Add isTextGroupPart and isToolGroupPart type guards to message-types.ts
- Replace `as TextGroupPart` and `as ToolGroupPart` assertions with proper type guards
- Provides runtime type safety for message part rendering

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/src/components/ai/shared/chat/message-types.ts (1)

30-57: Optional: Consider clarifying state type expansion.

The ToolPart interface defines 4 states while ToolGroupPart includes 6 states (adding 'done' and 'streaming'). While this appears intentional—ToolPart represents the AI SDK contract and ToolGroupPart represents the processed rendering state—a brief comment explaining when/why the additional states are used would improve clarity for future maintainers.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0370051 and 2e6d7bf.

📒 Files selected for processing (3)
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (5 hunks)
  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (5 hunks)
  • apps/web/src/components/ai/shared/chat/message-types.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
apps/web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/**/*.tsx: For document editing, register editing state using useEditingStore.getState().startEditing() and endEditing() to prevent unwanted UI refreshes
For AI streaming operations, register streaming state using useEditingStore.getState().startStreaming() and endStreaming() to prevent unwanted UI refreshes
When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
**/{components,src/**/components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for React component names and filenames

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-16T19:03:59.870Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the message parts structure with `{ parts: [{ type: 'text', text: '...' }] }` format for message content

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Always structure message content using the message parts structure: `{ parts: [{ type: 'text', text: '...' }] }`

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to **/*.{ts,tsx} : Message content must always use the message parts structure with type and text fields (e.g., `{ parts: [{ type: 'text', text: '...' }] }`)

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
  • apps/web/src/components/ai/shared/chat/message-types.ts
  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
📚 Learning: 2025-12-16T19:06:20.385Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/ui/Image.tsx:2-2
Timestamp: 2025-12-16T19:06:20.385Z
Learning: In apps/web/src/components/ai/ui/Image.tsx (TypeScript/React), the intentional use of `Experimental_GeneratedImage` from the Vercel AI SDK is accepted. This type is the correct and intended way to handle AI-generated images with base64/mediaType properties, and will be updated when the AI SDK stabilizes this API.

Applied to files:

  • apps/web/src/components/ai/shared/chat/MessageRenderer.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Keep commits and diffs minimal and focused on specific changes

Applied to files:

  • apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
🧬 Code graph analysis (2)
apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (3)
apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1)
  • useGroupedParts (17-72)
apps/web/src/components/ai/shared/chat/message-types.ts (2)
  • isTextGroupPart (80-82)
  • isToolGroupPart (87-89)
apps/web/src/components/ai/shared/chat/tool-calls/ToolCallRenderer.tsx (1)
  • ToolCallRenderer (71-212)
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (3)
apps/web/src/components/ai/shared/chat/useGroupedParts.ts (1)
  • useGroupedParts (17-72)
apps/web/src/components/ai/shared/chat/message-types.ts (2)
  • isTextGroupPart (80-82)
  • isToolGroupPart (87-89)
apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (1)
  • CompactToolCallRenderer (53-323)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Unit Tests
🔇 Additional comments (4)
apps/web/src/components/ai/shared/chat/message-types.ts (1)

64-89: LGTM! Well-implemented type guards.

The type guards use proper TypeScript type predicates, and the const assertion on VALID_TOOL_STATES correctly enables type derivation. The implementation is clean and type-safe.

apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (2)

1-14: LGTM! Clean migration to centralized types.

The imports correctly reference the new shared type definitions and the useGroupedParts hook. The refactoring maintains type safety while simplifying the component's responsibilities.

Also applies to: 237-237


331-369: LGTM! Rendering logic correctly implements individual tool call display.

The updated rendering flow uses the type guards properly to discriminate between text and tool parts, and renders each tool call individually with ToolCallRenderer (line 355). This aligns perfectly with the PR objective to remove grouping UI.

apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (1)

1-14: LGTM! Consistent implementation with compact styling.

The changes mirror MessageRenderer.tsx while maintaining the compact UI variants (CompactTextBlock, CompactToolCallRenderer). The use of useGroupedParts and type guards is consistent, and each tool call renders individually as intended.

Also applies to: 239-239, 332-370

The name ToolGroupPart was misleading as it represents a single
processed tool, not a group of tools. This rename clarifies that
it's the normalized/processed version of ToolPart for rendering.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit d36e3b0 into master Dec 20, 2025
3 checks passed
@2witstudios
2witstudios deleted the claude/remove-tool-grouping-ui-vgscG branch January 29, 2026 02:24
@coderabbitai coderabbitai Bot mentioned this pull request Apr 5, 2026
2 of 6 tasks
2witstudios added a commit that referenced this pull request Apr 8, 2026
… and tenant validation

The email regex /^[^\s@]+@[^\s@]+\.[^\s@]+$/ has polynomial backtracking
because [^\s@] matches '.', creating ambiguity in the domain portion.
Replaced with /^[^\s@]+@[^\s@.]+(\.[^\s@.]+)+$/ which excludes '.' from
domain character classes, eliminating backtracking. Adds ReDoS regression test.

Resolves CodeQL alerts #102, #103, #124.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Apr 8, 2026
… and tenant validation (#838)

The email regex /^[^\s@]+@[^\s@]+\.[^\s@]+$/ has polynomial backtracking
because [^\s@] matches '.', creating ambiguity in the domain portion.
Replaced with /^[^\s@]+@[^\s@.]+(\.[^\s@.]+)+$/ which excludes '.' from
domain character classes, eliminating backtracking. Adds ReDoS regression test.

Resolves CodeQL alerts #102, #103, #124.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants