Repository navigation
Remove tool call grouping UI and show individual calls - #102
Conversation
- 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.
WalkthroughReplaces inline message-part typing and handcrafted grouping with a shared Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🧰 Additional context used📓 Path-based instructions (6)**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/web/src/**/*.tsx📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/{components,src/**/components}/**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/web/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (8)📓 Common learnings📚 Learning: 2025-12-16T19:03:59.870ZApplied to files:
📚 Learning: 2025-12-14T14:54:15.319ZApplied to files:
📚 Learning: 2025-12-14T14:54:45.713ZApplied to files:
📚 Learning: 2025-12-14T14:54:47.122ZApplied to files:
📚 Learning: 2025-12-14T14:54:38.009ZApplied to files:
📚 Learning: 2025-12-14T14:54:45.713ZApplied to files:
📚 Learning: 2025-12-16T19:06:20.385ZApplied to files:
🧬 Code graph analysis (1)apps/web/src/components/ai/shared/chat/MessageRenderer.tsx (3)
⏰ 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)
🔇 Additional comments (5)
Comment |
There was a problem hiding this comment.
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
validStatesarray andisValidStatetype guard are duplicated verbatim inCompactMessageRenderer.tsx. Consider extracting these to a shared utility or theToolPart/ToolGroupParttype 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 thegroupedPartsuseMemo logic are nearly identical betweenMessageRenderer.tsxandCompactMessageRenderer.tsx. Consider:
- Extracting shared interfaces to a common types file
- Creating a shared hook (e.g.,
useGroupedParts) for the grouping logicThis would reduce maintenance burden and ensure consistency when the logic changes.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 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
anytypes in TypeScript code - always use proper TypeScript types
**/*.{ts,tsx}: Noanytypes - 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: importgetUserAccessLevelandcanUserEditPagefrom@pagespace/lib/permissions
Use Drizzle client from@pagespace/dbfor 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.tsxapps/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/permissionsfor access control, such asgetUserAccessLevel()andcanUserEditPage()
Always use Drizzle client from@pagespace/dbfor database access instead of direct database connections
Always use message parts structure withpartsarray containing objects withtypeandtextfields when constructing messages for AI
Files:
apps/web/src/components/ai/shared/chat/MessageRenderer.tsxapps/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 usinguseEditingStore.getState().startEditing()andendEditing()to prevent unwanted UI refreshes
For AI streaming operations, register streaming state usinguseEditingStore.getState().startStreaming()andendStreaming()to prevent unwanted UI refreshes
When using SWR, checkuseEditingStorestate withisAnyActive()and setisPausedto prevent data refreshes during editing or streaming
Files:
apps/web/src/components/ai/shared/chat/MessageRenderer.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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
ToolCallRendererinstead of the removedGroupedToolCallsRenderer, 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 existingToolCallsGroupParttype 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
isValidStateensures runtime safetyapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (3)
3-3: LGTM!Import updated correctly to use
CompactToolCallRendererinstead of the removedCompactGroupedToolCallsRenderer.
51-51: LGTM!Type definition correctly simplified, consistent with
MessageRenderer.tsx.
285-324: LGTM! Grouping logic correctly simplified.The implementation is consistent with
MessageRenderer.tsxand 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
There was a problem hiding this comment.
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 TextGroupPartat line 333 andas ToolGroupPartat 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
📒 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
anytypes in TypeScript code - always use proper TypeScript types
**/*.{ts,tsx}: Noanytypes - 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: importgetUserAccessLevelandcanUserEditPagefrom@pagespace/lib/permissions
Use Drizzle client from@pagespace/dbfor 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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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/permissionsfor access control, such asgetUserAccessLevel()andcanUserEditPage()
Always use Drizzle client from@pagespace/dbfor database access instead of direct database connections
Always use message parts structure withpartsarray containing objects withtypeandtextfields when constructing messages for AI
Files:
apps/web/src/components/ai/shared/chat/useGroupedParts.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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 usinguseEditingStore.getState().startEditing()andendEditing()to prevent unwanted UI refreshes
For AI streaming operations, register streaming state usinguseEditingStore.getState().startStreaming()andendStreaming()to prevent unwanted UI refreshes
When using SWR, checkuseEditingStorestate withisAnyActive()and setisPausedto prevent data refreshes during editing or streaming
Files:
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsapps/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 theincludes()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
useMemowith 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
ToolCallRendererfor individual tool calls.
12-13: LGTM - Centralized types and grouping logic.Good refactoring to import types from
./message-typesand use theuseGroupedPartshook. 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
useGroupedPartshook 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
There was a problem hiding this comment.
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
ToolPartinterface defines 4 states whileToolGroupPartincludes 6 states (adding'done'and'streaming'). While this appears intentional—ToolPartrepresents the AI SDK contract andToolGroupPartrepresents 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
📒 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
anytypes in TypeScript code - always use proper TypeScript types
**/*.{ts,tsx}: Noanytypes - 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: importgetUserAccessLevelandcanUserEditPagefrom@pagespace/lib/permissions
Use Drizzle client from@pagespace/dbfor 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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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/permissionsfor access control, such asgetUserAccessLevel()andcanUserEditPage()
Always use Drizzle client from@pagespace/dbfor database access instead of direct database connections
Always use message parts structure withpartsarray containing objects withtypeandtextfields when constructing messages for AI
Files:
apps/web/src/components/ai/shared/chat/MessageRenderer.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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 usinguseEditingStore.getState().startEditing()andendEditing()to prevent unwanted UI refreshes
For AI streaming operations, register streaming state usinguseEditingStore.getState().startStreaming()andendStreaming()to prevent unwanted UI refreshes
When using SWR, checkuseEditingStorestate withisAnyActive()and setisPausedto prevent data refreshes during editing or streaming
Files:
apps/web/src/components/ai/shared/chat/MessageRenderer.tsxapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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.tsxapps/web/src/components/ai/shared/chat/message-types.tsapps/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_STATEScorrectly 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
useGroupedPartshook. 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.tsxwhile maintaining the compact UI variants (CompactTextBlock,CompactToolCallRenderer). The use ofuseGroupedPartsand 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>
… 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>
… 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>
Each tool call now renders inline without grouping, providing a cleaner UI.
Summary by CodeRabbit
Refactor
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.