feat: Bubble support various message formats#123
Conversation
…nt with corresponding demos and documentation
- Added a new `message.vue` component to handle different message formats including text, markdown, and step-group messages. - Removed the deprecated `reasoning.vue` component and updated documentation to reflect the new message structure. - Enhanced the `Bubble` component to support rendering messages dynamically based on the new structure. - Introduced a message renderer system to manage different message types effectively.
- Updated the BubbleProps interface to clarify the handling of long text and words. - Modified the BubbleTextMessageRenderer to support line breaks in message content. - Removed the unused reasoning.svg file to clean up the assets.
- Updated the Message.vue component to use optional chaining for safer access to the renderer and component properties. - Added a TODO comment in markdown.ts to address DOMPurify handling for improved security in rendering markdown content.
- Changed the class name from `tr-bubble__step` to `tr-bubble__step-text` for better clarity in the TextStep component. - This update enhances the semantic structure of the component, making it more descriptive of its purpose.
- Updated the VitePress configuration to allow server access from all hosts. - Improved the BubbleTextMessageRenderer to enhance text display with better styling properties. - Added an IconSuccess component to the StepGroup for improved status indication. - Adjusted padding and gap in StepGroup styles for better layout consistency. - Enhanced ToolStep component with a new icon and improved title structure for clarity. - Added word-break styling to TextStep for better text handling.
- Updated the Bubble component to utilize a content renderer for improved markdown handling. - Introduced BubbleProvider for managing message renderers and added support for various message types. - Replaced the deprecated message.vue with messages.vue to streamline message management. - Added a new schema-card component for rendering schema data. - Enhanced the markdown renderer with DOMPurify for improved security. - Removed unused components and updated documentation to reflect the new structure.
- Replaced loading spinner with an image for better visual representation. - Streamlined the content rendering logic by simplifying template structures. - Enhanced the handling of messages and added conditional rendering for aborted states.
- Updated the messages.vue component to utilize a more flexible message renderer system. - Introduced a custom text renderer for improved message display options. - Enhanced the markdown renderer with error handling for better robustness. - Updated the BubbleProvider to support optional message renderers with default values. - Improved the tool message component to handle pretty JSON formatting and added new styles for better presentation.
- Simplified message content in messages.vue for clarity and conciseness. - Updated bubble.md to include detailed explanations of custom message renderers and their registration process. - Improved documentation for better understanding of the rendering mechanism and usage examples.
…uration - Added loading state support to the BubbleList component, allowing for dynamic loading indicators based on specified roles. - Updated loading.vue to include a loading message and a new BubbleList instance for better user feedback during data loading. - Enhanced documentation to clarify the usage of loading and loading-role properties in the BubbleList component.
- Updated the example in bubble.md to use 'extends' instead of 'implements' for the MyRenderer class, ensuring accurate representation of class inheritance for custom message renderers.
WalkthroughThis update introduces a modular and extensible message rendering system for bubble UI components. It adds new renderers, types, and a provider for custom message formats, refactors markdown and content rendering to use explicit renderer objects, and enhances documentation and demos. The changes include new Vue components, type definitions, CSS, and dependency updates. Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant BubbleProvider
participant BubbleList
participant Bubble
participant ContentItem
participant Renderer
App->>BubbleProvider: Provide custom/content renderers
BubbleProvider->>BubbleList: Context: renderer map
BubbleList->>Bubble: Render each message
Bubble->>ContentItem: For each content item (if array)
ContentItem->>Renderer: Lookup renderer by type, render content
Renderer-->>ContentItem: Returns VNode/component
ContentItem-->>Bubble: Rendered content
Bubble-->>BubbleList: Rendered bubble
BubbleList-->>App: Rendered message list
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
docs/demos/bubble/markdown.vueOops! Something went wrong! :( ESLint: 9.32.0 Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it. docs/demos/bubble/schema-render.vueOops! Something went wrong! :( ESLint: 9.32.0 Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it. docs/demos/bubble/streaming.vueOops! Something went wrong! :( ESLint: 9.32.0 Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 9
🔭 Outside diff range comments (1)
packages/components/src/bubble/Bubble.vue (1)
140-154: Fix inconsistent CSS class namingMultiple CSS class names use
tr-bubbuleinstead oftr-bubble, which may cause styling issues.- .tr-bubbule__body-text { + .tr-bubble__body-text { color: rgb(25, 25, 25); font-size: 16px; line-height: 26px; word-break: break-word; white-space: pre-line; } - .tr-bubbule__aborted { + .tr-bubble__aborted { color: rgb(128, 128, 128); font-size: 14px; } - .tr-bubbule__footer { + .tr-bubble__footer { margin-top: 12px; }Also update the template references to match:
- <span v-if="props.aborted" class="tr-bubbule__aborted">(用户停止)</span> - <div v-if="slots.footer" class="tr-bubbule__footer"> + <span v-if="props.aborted" class="tr-bubble__aborted">(用户停止)</span> + <div v-if="slots.footer" class="tr-bubble__footer">
🧹 Nitpick comments (10)
packages/components/src/bubble/message/class-renderer.ts (1)
4-6: Consider using generics for better type safety.The abstract class design is solid, but the
anytype usage could be improved. Consider using generics to provide better type safety for implementers.-export abstract class BubbleMessageClassRenderer { - abstract render(options: { [key: string]: any }): VNode +export abstract class BubbleMessageClassRenderer<T = Record<string, any>> { + abstract render(options: T): VNodeThis allows implementers to specify their expected options type while maintaining flexibility.
packages/components/src/bubble/message/collapse.vue (2)
10-10: Consider renaming the variable for clarity.The variable
collapsedis initialized tofalse, meaning the component starts in an expanded state. Consider renaming it toexpandedorisExpandedfor better semantic clarity.-const collapsed = ref(false) +const expanded = ref(true)And update the template accordingly:
- <IconArrowUp class="expand-icon" :class="{ 'rotate-180': collapsed }" @click="collapsed = !collapsed" /> + <IconArrowUp class="expand-icon" :class="{ 'rotate-180': !expanded }" @click="expanded = !expanded" />- <div v-show="!collapsed" class="tr-bubble__step-text-content">{{ props.content }}</div> + <div v-show="expanded" class="tr-bubble__step-text-content">{{ props.content }}</div>
61-68: Add transition property for smoother icon rotation.The icon rotation would benefit from a smooth transition effect.
.expand-icon { font-size: 14px; cursor: pointer; + transition: transform 0.2s ease-in-out; &.rotate-180 { transform: rotate(180deg); } }docs/demos/bubble/messages.vue (1)
87-98: Consider cleanup for setTimeout intervals.The typing animation creates multiple setTimeout calls but doesn't provide cleanup if the component unmounts. Consider storing timeout IDs and clearing them in onUnmounted.
+import { h, ref, onUnmounted } from 'vue' + +const timeoutIds = ref<number[]>([]) + +onUnmounted(() => { + timeoutIds.value.forEach(clearTimeout) +}) const setThinkingContent = () => { const message = messages.value.find((m) => m.type === 'collapse') if (!message) { return } message.content = '' + timeoutIds.value.forEach(clearTimeout) + timeoutIds.value = [] for (let i = 0; i < thinkingContent.length; i += 1) { - setTimeout(() => { + const id = setTimeout(() => { message.content += thinkingContent[i] }, i * 100) + timeoutIds.value.push(id) } }docs/demos/bubble/schema-render.vue (1)
21-24: Document security implications of allowing custom tagsWhile appropriate for this demo, allowing custom tags and attributes in production environments requires careful consideration.
Consider adding a comment to clarify the security implications:
const markdownRenderer = new BubbleMarkdownMessageRenderer( { html: true }, + // WARNING: Only allow trusted tags/attributes. This configuration is for demo purposes. { ADD_TAGS: ['schema-card'], ADD_ATTR: ['schema'] }, )packages/components/src/bubble/message/tool.vue (2)
49-65: Ensure JSON highlighting is XSS-safeThe regex-based JSON highlighting generates HTML that's rendered via v-html. While the content is JSON-stringified first (which escapes HTML), verify that the highlighting logic doesn't introduce vulnerabilities.
The current implementation appears safe because JSON.stringify escapes HTML characters, but consider using a dedicated JSON syntax highlighting library for better maintainability.
Also applies to: 84-84
39-45: Optimize JSON stringification logicThe current implementation parses JSON strings just to stringify them again, which is inefficient.
try { - if (typeof json === 'string') { - prettyJson = JSON.stringify(JSON.parse(json), null, space) - } else { - prettyJson = JSON.stringify(json, null, space) - } + prettyJson = typeof json === 'string' ? json : JSON.stringify(json, null, space) + // Validate if it's already a JSON string + if (typeof json === 'string') { + try { + JSON.parse(json) + } catch { + // If not valid JSON, stringify it + prettyJson = JSON.stringify(json, null, space) + } + } } catch (error) {packages/components/src/bubble/index.type.ts (2)
20-21: Clarify the contentRenderer precedence comment.The comment explaining when
contentRendereris ineffective could be clearer about the precedence rules.Consider rewording for better clarity:
- * 气泡内容渲染器。 - * 如果 Bubble 中的 messages 长度大于 0,则 contentRenderer 无效。将会使用 BubbleProvider 中注册的渲染器 + * 气泡内容渲染器。 + * 注意:当 messages 数组不为空时,contentRenderer 将被忽略,优先使用 BubbleProvider 中注册的渲染器
32-36: Consider adding validation constraints to the messages array.The
messagesproperty allows an array but doesn't specify constraints. Consider if there should be validation rules or constraints documented.If there are business rules about message array usage (e.g., mutual exclusivity with
content), consider documenting them:/** * 气泡内容 */ content?: string + /** + * 气泡消息数组。当设置时,content 属性将被忽略 + */ messages?: BubbleMessageProps[]docs/src/components/bubble.md (1)
84-84: Fix Chinese grammar in documentation.The static analysis tool correctly identified a grammar issue in the Chinese text.
Apply this correction:
- 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 + 类渲染器通常用来复用复杂度较高地渲染器,比如MarkdownIt实例
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/svgs/src/assets/success.svgis excluded by!**/*.svgpackages/svgs/src/assets/tool-running.svgis excluded by!**/*.svg
📒 Files selected for processing (27)
docs/demos/bubble/loading.vue(1 hunks)docs/demos/bubble/markdown.vue(1 hunks)docs/demos/bubble/messages.vue(1 hunks)docs/demos/bubble/schema-render.vue(1 hunks)docs/demos/bubble/shape.vue(1 hunks)docs/demos/bubble/slots.vue(1 hunks)docs/demos/bubble/streaming.vue(1 hunks)docs/src/components/bubble.md(6 hunks)packages/components/package.json(1 hunks)packages/components/src/bubble/Bubble.vue(5 hunks)packages/components/src/bubble/BubbleList.vue(1 hunks)packages/components/src/bubble/BubbleProvider.vue(1 hunks)packages/components/src/bubble/index.ts(2 hunks)packages/components/src/bubble/index.type.ts(2 hunks)packages/components/src/bubble/message/Message.vue(1 hunks)packages/components/src/bubble/message/class-renderer.ts(1 hunks)packages/components/src/bubble/message/collapse.ts(1 hunks)packages/components/src/bubble/message/collapse.vue(1 hunks)packages/components/src/bubble/message/defaultRendererMap.ts(1 hunks)packages/components/src/bubble/message/index.ts(1 hunks)packages/components/src/bubble/message/index.type.ts(1 hunks)packages/components/src/bubble/message/markdown.ts(1 hunks)packages/components/src/bubble/message/text.ts(1 hunks)packages/components/src/bubble/message/text.vue(1 hunks)packages/components/src/bubble/message/tool.ts(1 hunks)packages/components/src/bubble/message/tool.vue(1 hunks)packages/components/src/index.ts(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/components/src/bubble/message/class-renderer.ts (2)
packages/components/src/bubble/index.ts (1)
BubbleMessageClassRenderer(5-5)packages/components/src/index.ts (1)
BubbleMessageClassRenderer(71-71)
packages/components/src/bubble/index.ts (1)
packages/components/src/index.ts (2)
BubbleProvider(68-68)BubbleProvider(69-69)
packages/components/src/index.ts (1)
packages/components/src/bubble/index.ts (1)
BubbleProvider(35-37)
packages/components/src/bubble/message/defaultRendererMap.ts (4)
packages/components/src/bubble/message/index.type.ts (1)
BubbleMessageRenderer(7-7)packages/components/src/bubble/message/text.ts (1)
BubbleTextMessageRenderer(3-3)packages/components/src/bubble/message/tool.ts (1)
BubbleToolMessageRenderer(3-3)packages/components/src/bubble/message/collapse.ts (1)
BubbleCollapseMessageRenderer(3-3)
packages/components/src/bubble/index.type.ts (1)
packages/components/src/bubble/message/index.type.ts (2)
BubbleMessageRenderer(7-7)BubbleMessageProps(9-12)
🪛 LanguageTool
docs/src/components/bubble.md
[uncategorized] ~84-~84: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:高"地"渲染
Context: ...sageClassRenderer` 类 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 ```typescript ...
(wb4)
🔇 Additional comments (32)
packages/components/package.json (1)
30-30: Verify the dompurify version for security and currency.The addition of dompurify for HTML sanitization in markdown rendering is appropriate. However, ensure this version is current and free from known vulnerabilities.
What is the latest version of dompurify and are there any known security vulnerabilities in version 3.2.6?docs/demos/bubble/slots.vue (1)
10-10: LGTM - Clear label improvement.The simplified label text improves clarity by removing the redundant "content" qualifier.
packages/components/src/bubble/message/text.vue (1)
1-17: LGTM - Well-implemented text renderer component.The component follows Vue 3 best practices with:
- Proper TypeScript prop typing
- Appropriate CSS styling for text display
- Good UX considerations with
word-break: break-wordandwhite-space: pre-line- Clean, focused implementation
packages/components/src/bubble/message/text.ts (1)
1-3: LGTM - Clean export pattern.The simple re-export with a descriptive name follows good modular architecture practices and maintains consistency with the renderer naming convention.
packages/components/src/bubble/message/tool.ts (1)
1-3: LGTM! Clean export pattern.The module correctly follows the established pattern for message renderer exports with clear naming conventions.
packages/components/src/bubble/message/collapse.ts (1)
1-3: LGTM! Consistent with established patterns.The module maintains consistency with other renderer exports and follows the clean, minimal approach.
packages/components/src/bubble/message/index.ts (1)
1-7: LGTM! Proper barrel export implementation.The index file correctly consolidates all message renderer exports using the standard barrel export pattern, providing a clean single entry point for the message rendering system.
docs/demos/bubble/shape.vue (1)
1-21: LGTM! Well-structured demo showcasing shape variations.The demo effectively demonstrates the new shape functionality with clear examples of both shape options and placements. The component structure is clean and appropriate for a demo.
docs/demos/bubble/markdown.vue (2)
2-2: LGTM! Correct implementation of new renderer architecture.The template properly uses the new
content-rendererprop with an explicit renderer instance, demonstrating the architectural shift from type-based to renderer-based message handling.
6-12: LGTM! Proper renderer instantiation and usage.The script correctly imports and instantiates the
BubbleMarkdownMessageRenderer, demonstrating the new modular approach to message rendering. The implementation aligns well with the new architecture.packages/components/src/bubble/index.ts (2)
4-5: LGTM! Clean implementation following established patterns.The new BubbleProvider component registration and message renderer exports follow the same patterns as existing components in the file. The implementation is consistent and well-structured.
27-37: LGTM! Component registration follows established conventions.The BubbleProvider component registration with install method follows the exact same pattern as the existing Bubble and BubbleList components, ensuring consistency across the codebase.
docs/demos/bubble/streaming.vue (1)
2-2: LGTM! Proper migration to the new renderer-based API.The update correctly migrates from the old
type="markdown"prop to the newcontent-rendererprop with an explicitBubbleMarkdownMessageRendererinstance. This demonstrates the breaking change introduced in the PR and follows the new provider-based architecture.Also applies to: 8-8, 14-14
packages/components/src/bubble/BubbleProvider.vue (3)
11-18: LGTM! Clean props definition with appropriate defaults.The props interface is well-defined with proper TypeScript typing and sensible defaults. The use of
withDefaultsensures that the component works correctly when no custom renderers are provided.
20-28: LGTM! Efficient renderer map merging logic.The renderer map initialization correctly merges default renderers with custom ones, allowing users to override defaults while maintaining a clean fallback structure. The use of
Mapis appropriate for efficient lookups.
30-31: LGTM! Proper use of Vue's provide/inject pattern.The provider setup correctly exposes both the renderer map and fallback renderer using distinct keys, enabling child components to inject the appropriate renderer context.
packages/components/src/index.ts (3)
4-4: LGTM! Proper import of new bubble components and utilities.The import statement correctly includes the new BubbleProvider component and message renderer classes from the bubble module.
36-36: LGTM! BubbleProvider added to global component registration.Adding BubbleProvider to the components array ensures it gets registered globally during plugin installation, maintaining consistency with other components.
68-71: LGTM! Consistent export pattern for new bubble utilities.The exports follow the established pattern of providing both the original name and the
Trprefixed alias, ensuring API consistency across the component library.packages/components/src/bubble/message/defaultRendererMap.ts (1)
1-15: Clean and well-structured renderer registry implementation.The default renderer map provides a solid foundation for the extensible message rendering system. The use of Map for O(1) lookups and unique Symbol keys for dependency injection follows Vue best practices.
packages/components/src/bubble/BubbleList.vue (2)
36-44: Well-implemented loading state logic.The computed property correctly validates all required conditions before creating the loading bubble configuration. The destructuring and null return provide clean integration with Vue's conditional rendering.
49-60: Proper conditional rendering and consistent slot handling.The template changes correctly handle hidden items and maintain consistent slot rendering patterns between regular and loading bubbles. The wrapping template approach is appropriate for conditional rendering within v-for.
docs/demos/bubble/messages.vue (1)
1-99: Excellent demonstration of the new message rendering architecture.This demo effectively showcases the flexibility of function, class, and component renderers. The variety of message types and interactive features provide a comprehensive example of the new capabilities.
packages/components/src/bubble/message/index.type.ts (1)
1-13: Well-designed type definitions for extensible architecture.The type definitions properly balance type safety with flexibility. The union type for renderers supports all three implementation patterns (function, class, component), and the use of
anyin BubbleMessageProps is appropriate for the extensible message system.docs/demos/bubble/loading.vue (2)
8-9: Proper demonstration of bubble list loading state.The bubble list integration correctly demonstrates the new loading functionality with appropriate prop bindings and role configuration.
27-43: Clean configuration of items and roles.The reactive data setup properly types the items array and provides comprehensive role configuration with placement and avatars. This demonstrates the role-based avatar system effectively.
packages/components/src/bubble/index.type.ts (2)
2-3: LGTM! Clean imports and re-exports.The import structure and re-export pattern follow good TypeScript practices for modular type organization.
5-25: Excellent refactoring with proper interface hierarchy.The extraction of
BubbleCommonPropsand the extension pattern used inBubblePropsandBubbleRoleConfigdemonstrates good TypeScript design. This promotes code reusability and maintains consistency across the component hierarchy.Also applies to: 27-37, 45-47
docs/src/components/bubble.md (4)
59-118: Comprehensive documentation of the new message rendering system.This section excellently documents the new multi-format message rendering architecture. The explanation of the three renderer types (function, class, component) with practical examples is very helpful for developers. The code examples are clear and demonstrate proper usage patterns.
23-27: Clear documentation of new features.The shape property documentation and updated loading behavior explanation are concise and accurate, providing users with the necessary information to understand these new features.
Also applies to: 33-34
213-223: Ensure consistency between BubbleMessageProps documentation and type definition.The documented interface structure should match the imported type exactly.
Let me verify the BubbleMessageProps interface matches what's documented:
#!/bin/bash # Check the actual BubbleMessageProps interface definition echo "Checking BubbleMessageProps interface..." rg -A 10 "interface BubbleMessageProps" packages/components/src/bubble/message/
156-162: ```shell
#!/bin/bashSearch for a BubblePlacement type alias in the codebase
rg "type BubblePlacement" -n .
</details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
- Updated class names from `tr-bubbule` to `tr-bubble` in Bubble.vue for improved consistency and clarity in styling. - Adjusted related CSS styles to reflect the new class naming convention.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
packages/components/src/bubble/Bubble.vue (2)
78-78: The typo in class name has been fixed.Good catch on fixing the class name from
tr-bubbule__body-texttotr-bubble__body-textas mentioned in the past review comments.
81-81: The hardcoded Chinese text for aborted message still needs internationalization support.The aborted message remains hardcoded in Chinese, which was flagged in previous reviews but hasn't been addressed yet.
Consider implementing the previously suggested solution:
const props = withDefaults(defineProps<BubbleProps>(), { content: '', placement: 'start', shape: 'corner', maxWidth: '80%', + abortedText: '(User stopped)', }) - <span v-if="props.aborted" class="tr-bubble__aborted">(用户停止)</span> + <span v-if="props.aborted" class="tr-bubble__aborted">{{ props.abortedText }}</span>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/components/src/bubble/Bubble.vue(5 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/components/src/bubble/Bubble.vue (1)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.773Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
🔇 Additional comments (5)
packages/components/src/bubble/Bubble.vue (5)
4-6: LGTM! Clean import organization for the new renderer system.The imports are well-organized and support the new message rendering architecture effectively.
63-67: LGTM! Well-structured multiple message rendering.The implementation for rendering multiple messages is clean and follows Vue best practices with proper key binding.
70-77: LGTM! Proper renderer handling with fallback logic.The conditional rendering logic correctly handles different renderer types and provides appropriate fallback behavior.
58-62: LGTM! Consistent shape-based styling implementation.The conditional CSS class application based on the
shapeprop is implemented consistently for both loading and content states.
134-146: LGTM! Well-organized styles for the new features.The CSS additions properly support the multiple message layout and maintain consistent spacing and typography.
… renderer - Updated message type from 'collapse' to 'collapsible-text' in messages.vue for clarity. - Removed deprecated collapse renderer and added new collapsible-text renderer for improved functionality. - Enhanced bubble.md documentation to reflect the new renderer type and usage. - Updated defaultRendererMap to include the new collapsible-text renderer.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
docs/src/components/bubble.md (5)
23-28: Shape section looks good but can mention fallback behaviourNice addition — it’s immediately clear how to switch between
roundedandcorner.
Consider adding one sentence that clarifies what happens when an invalid value is passed (falls back tocorner, throws, etc.) so users don’t have to look at the implementation.
59-69: Minor markdown formatting nitThe numbered list is rendered as “1. 1. 1. …”.
Prefix each item with1.is fine for GitHub MD, but VuePress sometimes preserves the actual numbers.
Safer to write:1. 检查每条消息的 `type` 字段 2. 在 `BubbleProvider` 中查找匹配的渲染器 3. 使用找到的渲染器渲染消息内容 4. 如果未找到匹配的渲染器,则使用默认渲染方式
70-79: Example missing explicithimport
hcomes fromvue. Without the import the snippet will not compile when readers copy-paste.-const myRenderer: BubbleMessageFunctionRenderer = (options) => { - return h('div', options.content) -} +import { h } from 'vue' + +const myRenderer: BubbleMessageFunctionRenderer = (options) => + h('div', options.content)
81-92: Grammar / wording polish“复用复杂度较高的渲染器” → “复用复杂度较高的渲染器” (去掉多余的“度”) to satisfy the LanguageTool hint and read more naturally.
94-106: Avoid Chinglish: “new 一个实例”“注册时记得 new 一个实例” → “注册时记得创建实例”
-注册时记得 new 一个实例,否则会导致渲染失败 +注册时记得创建实例,否则会导致渲染失败
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
docs/demos/bubble/messages.vue(1 hunks)docs/src/components/bubble.md(6 hunks)packages/components/src/bubble/message/collapsible-text.ts(1 hunks)packages/components/src/bubble/message/collapsible-text.vue(1 hunks)packages/components/src/bubble/message/defaultRendererMap.ts(1 hunks)packages/components/src/bubble/message/index.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- packages/components/src/bubble/message/collapsible-text.ts
- packages/components/src/bubble/message/collapsible-text.vue
🚧 Files skipped from review as they are similar to previous changes (2)
- docs/demos/bubble/messages.vue
- packages/components/src/bubble/message/defaultRendererMap.ts
🧰 Additional context used
🪛 LanguageTool
docs/src/components/bubble.md
[uncategorized] ~84-~84: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:高"地"渲染
Context: ...sageClassRenderer` 类 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 ```typescript ...
(wb4)
🔇 Additional comments (2)
packages/components/src/bubble/message/index.ts (1)
1-7: Well-structured barrel export pattern for the message rendering system.This index file follows best practices for consolidating module exports and provides a clean API for the new message rendering architecture. The approach aligns well with the PR objectives of supporting various message formats through a modular, extensible system.
docs/src/components/bubble.md (1)
161-173: Table column separator escape is already correct – good jobThe
|in'rounded' \| 'corner'is properly escaped, preventing the table from breaking.
Nothing to change here.
…tion - Eliminated the contentRenderer prop and related logic from Bubble.vue to streamline the rendering process. - Updated bubble.md documentation to reflect the removal of contentRenderer and its implications for bubble content handling. - Adjusted index.type.ts to remove references to the deprecated contentRenderer type.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
docs/src/components/bubble.md (1)
33-34:loading-role仍与 API 表格不一致
文档叙述继续使用loading-role(kebab-case),而下方 API 表格第 214 行使用的是loadingRole(camelCase)。请保持一致,否则读者会误以为存在两个不同的属性。
🧹 Nitpick comments (3)
docs/src/components/bubble.md (3)
84-85: 语序略显生硬,可读性欠佳
“复用复杂度较高的渲染器” 建议改成 “复用复杂且通用的渲染器” 或 “复用复杂度较高的逻辑”。-类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 +类渲染器通常用于复用复杂且通用的渲染器,例如 MarkdownIt 实例
118-126: 重复用字 & 名称缺失
- “直接可用的的” 出现连写。
- 标题 “内置需要自行导入的渲染有” 缺少 “器”,建议同时调整语序。
-目前内置直接可用的的渲染器类型有 +目前内置即可直接使用的渲染器类型有 … -内置需要自行导入的渲染有 +内置但需手动导入的渲染器有
96-104: 代码示例缺少messageRenderers来源 & 组件名与前文不统一
示例直接引用<tr-bubble-provider>,而上文均称 BubbleProvider,名称不统一;且未展示messageRenderers的导入/声明,读者无法快速复现。建议:-<tr-bubble-provider :message-renderers="messageRenderers"> +<BubbleProvider :message-renderers="messageRenderers">并在
<script setup>或常规<script>中给出完整示例:<script setup lang="ts"> import { BubbleProvider, BubbleMarkdownMessageRenderer } from '@opentiny/tiny-robot' const messageRenderers = { markdown: new BubbleMarkdownMessageRenderer(), // 其他渲染器…… } </script>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
docs/src/components/bubble.md(6 hunks)packages/components/src/bubble/Bubble.vue(5 hunks)packages/components/src/bubble/index.type.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/components/src/bubble/Bubble.vue
- packages/components/src/bubble/index.type.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/bubble.md (1)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
🪛 LanguageTool
docs/src/components/bubble.md
[uncategorized] ~84-~84: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:高"地"渲染
Context: ...sageClassRenderer` 类 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 ```typescript ...
(wb4)
…mplification" This reverts commit fd2176f.
- Renamed the `collapsed` state to `expanded` for clarity in the tool component. - Adjusted the expand/collapse icon and conditional rendering to reflect the new state management. - Improved readability and maintainability of the component's code.
…ment selector in container - Added `word-break: break-word;` to the tool component's CSS for better text handling. - Modified the slotted element selector in the container component for improved flexibility in layout.
…G icons - Added a button to toggle tool status in the messages demo. - Introduced reactive tool message handling with various statuses (running, success, failed, cancelled). - Updated tool message rendering to use new SVG icons for different statuses. - Added loading and error SVG assets, and removed the obsolete tool-running SVG. - Included CSS animations for loading state.
… last bubble messages
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
packages/components/src/bubble/message/tool.vue (1)
14-23: Add internationalization support for status textThe status text is hardcoded in Chinese, limiting the component's reusability in different locales.
docs/src/components/bubble.md (1)
33-34: Inconsistent prop name:loading-rolevsloadingRoleThe narrative uses kebab-case (
loading-role), while the API table below and the TS typings defineloadingRole(camelCase). This inconsistency will confuse users and break auto-completion.
🧹 Nitpick comments (1)
packages/components/src/bubble/message/tool.vue (1)
27-61: Consider refactoring JSON highlighting for better maintainability.The JSON highlighting logic works correctly but could be improved:
- The complex regex pattern is hard to maintain and debug
- Error handling only logs to console without user feedback
- Consider using a dedicated syntax highlighting library for better reliability
Consider using a library like
prismjsorhighlight.jsfor more robust syntax highlighting:-const highlightJSON = <T extends string | object>(json?: T): string => { - // complex regex implementation -} +import { highlight, languages } from 'prismjs' + +const highlightJSON = <T extends string | object>(json?: T): string => { + if (!json) return '' + + let prettyJson = '' + const space = props.formatPretty ? 2 : 0 + + try { + if (typeof json === 'string') { + prettyJson = JSON.stringify(JSON.parse(json), null, space) + } else { + prettyJson = JSON.stringify(json, null, space) + } + return highlight(prettyJson, languages.json, 'json') + } catch (error) { + console.warn('JSON parsing error:', error) + return typeof json === 'string' ? json : String(json) + } +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
packages/svgs/src/assets/cancelled.svgis excluded by!**/*.svgpackages/svgs/src/assets/error.svgis excluded by!**/*.svgpackages/svgs/src/assets/loading.svgis excluded by!**/*.svgpackages/svgs/src/assets/plugin.svgis excluded by!**/*.svg
📒 Files selected for processing (11)
docs/demos/bubble/messages.vue(1 hunks)docs/src/components/bubble.md(6 hunks)packages/components/package.json(1 hunks)packages/components/src/bubble/Bubble.vue(5 hunks)packages/components/src/bubble/BubbleList.vue(2 hunks)packages/components/src/bubble/index.type.ts(2 hunks)packages/components/src/bubble/message/tool.vue(1 hunks)packages/components/src/container/index.vue(1 hunks)packages/components/src/index.ts(3 hunks)packages/components/src/styles/animations.css(1 hunks)packages/components/src/styles/root.css(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- packages/components/src/styles/root.css
- packages/components/src/styles/animations.css
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/components/package.json
- packages/components/src/index.ts
- docs/demos/bubble/messages.vue
- packages/components/src/bubble/BubbleList.vue
- packages/components/src/bubble/Bubble.vue
- packages/components/src/bubble/index.type.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/bubble/message/tool.vue (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/bubble.md (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
🪛 LanguageTool
docs/src/components/bubble.md
[uncategorized] ~84-~84: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:高"地"渲染
Context: ...sageClassRenderer` 类 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 ```typescript ...
(wb4)
🔇 Additional comments (7)
packages/components/src/bubble/message/tool.vue (3)
1-12: LGTM! Well-structured component setup.The props definition and imports are clean and follow Vue 3 best practices. The flexible content type appropriately handles various tool invocation data formats.
78-81: Security consideration: v-html usage with dynamic content.The
v-htmldirective is used to render highlighted JSON. While the content comes from the internalhighlightJSONfunction which should be safe, ensure that any user-provided content inprops.contentis properly sanitized to prevent XSS attacks.The current implementation should be safe since
highlightJSONonly processes JSON data, but consider adding explicit sanitization if the component might receive untrusted content in the future.
85-181: LGTM! Well-organized styling approach.The combination of scoped LESS styles for layout and CSS modules for syntax highlighting colors is well-executed. The styling includes proper responsive design, animations, and good visual hierarchy.
docs/src/components/bubble.md (4)
23-27: LGTM! Clear documentation of the new shape feature.The bubble shape section clearly explains the available options and default behavior.
59-128: Excellent comprehensive documentation of the new message rendering system.This section thoroughly documents the core feature of the PR - the modular message rendering architecture. It clearly explains:
- The renderer resolution process
- Three implementation approaches with examples
- Important implementation details (like class instantiation)
- Available built-in renderers
The documentation provides developers with everything needed to understand and use the new system.
140-140: LGTM! Custom element variant supports enhanced functionality.The change to use
.ce.vuefile variant aligns with the enhanced markdown renderer capabilities for custom elements.
161-268: Comprehensive and well-structured API documentation.The API documentation thoroughly covers all the new interfaces and types introduced in the enhanced bubble system:
- Clear inheritance relationships (BubbleCommonProps)
- Complete property descriptions with types and defaults
- Detailed renderer type definitions
- Proper documentation of the message system architecture
This provides developers with a complete reference for the new capabilities.
…essages' - Changed the bubble component to accept 'content' as an array of messages. - Updated related components and documentation to reflect this change. - Enhanced the logic for rendering messages based on the new 'content' prop.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
docs/src/components/bubble.md (1)
33-34: Prop name inconsistency persists (loading-rolevsloadingRole)The narrative still uses kebab-case
loading-role, but the API table and TS typings use camelCaseloadingRole. This mismatch will confuse readers and break auto-completion examples.
🧹 Nitpick comments (1)
docs/src/components/bubble.md (1)
70-96: Importhin code snippet for completenessThe function-style renderer example calls
hbut doesn’t show where it comes from. Beginners may copy-paste and run into “his not defined”. Consider prependingimport { h } from 'vue'.-const myRenderer: BubbleMessageFunctionRenderer = (options) => { - return h('div', options.content) -} +import { h } from 'vue' + +const myRenderer: BubbleMessageFunctionRenderer = (options) => { + return h('div', options.content) +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
docs/demos/bubble/messages.vue(1 hunks)docs/src/components/bubble.md(6 hunks)packages/components/src/bubble/Bubble.vue(5 hunks)packages/components/src/bubble/BubbleList.vue(2 hunks)packages/components/src/bubble/index.type.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/components/src/bubble/BubbleList.vue
- docs/demos/bubble/messages.vue
- packages/components/src/bubble/Bubble.vue
- packages/components/src/bubble/index.type.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/bubble.md (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
🪛 LanguageTool
docs/src/components/bubble.md
[uncategorized] ~84-~84: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:高"地"渲染
Context: ...MessageClassRenderer` 类 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 ```typescript class ...
(wb4)
🔇 Additional comments (1)
docs/src/components/bubble.md (1)
23-28:shapesection is clear and matches implementationThe new “气泡形状” section accurately documents the
shapeprop, its options, and default value. No issues spotted.
…ntentRenderer - Updated the bubble component and related files to utilize the new BubbleMarkdownContentRenderer. - Changed the prop names from 'messageRenderers' to 'contentRenderers' for consistency. - Refactored the content handling to support the new structure with BubbleContentItem. - Removed obsolete message-related files and updated documentation accordingly.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
packages/components/src/bubble/Bubble.vue (2)
94-94: Add internationalization support for aborted message.The aborted message is hardcoded in Chinese, which lacks internationalization support.
This is the same issue flagged in previous reviews. Consider accepting the aborted text as a prop or using an i18n solution to enable localization.
17-34: Add error handling to contentRenderer computed property.The contentRenderer resolution logic lacks error handling for different renderer types, which could lead to runtime issues if renderers fail.
This is similar to the issue flagged in previous reviews. The renderer resolution should include try-catch blocks and proper type guards to handle errors gracefully and prevent runtime failures.
docs/src/components/bubble.md (1)
33-34: Keep prop naming consistent: useloadingRoleinstead ofloading-roleThe narrative still uses the kebab-case form while the API table (l. 216) and TS typings expose
loadingRole(camelCase). Mixing the two will confuse users.-BubbleList 除了需要设置 `loading`,还需要设置 `loading-role`。需要注意的是,列表的加载中气泡实际上并没有新增一条消息,`loading` 设置为 `false` 后,加载中的气泡不会渲染 +BubbleList 除了需要设置 `loading`,还需要设置 `loadingRole`(模板中可写作 `loading-role`)。需要注意的是,列表的加载中气泡实际上并没有新增一条消息,`loading` 设置为 `false` 后,加载中的气泡不会渲染
🧹 Nitpick comments (2)
packages/components/src/bubble/renderers/markdown.ts (1)
21-26: Consider sanitizing fallback content when sanitization is enabled.The error fallback returns raw content without sanitization, which could pose a security risk if the original content contained malicious HTML and sanitization is enabled.
Apply this diff to ensure consistent sanitization:
try { htmlContent = this.md.render(options.content ?? '') } catch (error) { console.error('Error rendering markdown:', error) - htmlContent = options.content ?? '' + htmlContent = options.content ?? '' + // Apply sanitization to fallback content if enabled + if (!this.dompurifyConfig.disable) { + htmlContent = DOMPurify.sanitize(htmlContent, this.dompurifyConfig) + } }docs/src/components/bubble.md (1)
118-126: Tidy up typos in the built-in renderer listSmall wording issues slip in here:
- “的的” – duplicated token.
- Missing “器” after “渲染”.
- Better parallelism for the two-part list.
-目前内置直接可用的的渲染器类型有 +目前内置直接可用的渲染器类型有 -内置需要自行导入的渲染有 +需要手动导入的内置渲染器有
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
docs/demos/bubble/markdown.vue(1 hunks)docs/demos/bubble/messages.vue(1 hunks)docs/demos/bubble/schema-render.vue(1 hunks)docs/demos/bubble/streaming.vue(1 hunks)docs/src/components/bubble.md(6 hunks)packages/components/src/bubble/Bubble.vue(5 hunks)packages/components/src/bubble/BubbleProvider.vue(1 hunks)packages/components/src/bubble/components/ContentItem.vue(1 hunks)packages/components/src/bubble/components/index.ts(1 hunks)packages/components/src/bubble/index.ts(2 hunks)packages/components/src/bubble/index.type.ts(2 hunks)packages/components/src/bubble/renderers/class-renderer.ts(1 hunks)packages/components/src/bubble/renderers/collapsible-text.ts(1 hunks)packages/components/src/bubble/renderers/collapsible-text.vue(1 hunks)packages/components/src/bubble/renderers/defaultRendererMap.ts(1 hunks)packages/components/src/bubble/renderers/index.ts(1 hunks)packages/components/src/bubble/renderers/index.type.ts(1 hunks)packages/components/src/bubble/renderers/markdown.ts(1 hunks)packages/components/src/bubble/renderers/text.ts(1 hunks)packages/components/src/bubble/renderers/text.vue(1 hunks)packages/components/src/bubble/renderers/tool.ts(1 hunks)packages/components/src/bubble/renderers/tool.vue(1 hunks)packages/components/src/index.ts(3 hunks)
✅ Files skipped from review due to trivial changes (9)
- packages/components/src/bubble/components/index.ts
- packages/components/src/bubble/renderers/tool.ts
- packages/components/src/bubble/renderers/collapsible-text.ts
- packages/components/src/bubble/renderers/class-renderer.ts
- packages/components/src/bubble/renderers/index.ts
- packages/components/src/bubble/renderers/collapsible-text.vue
- packages/components/src/bubble/renderers/text.ts
- packages/components/src/bubble/renderers/defaultRendererMap.ts
- packages/components/src/bubble/renderers/index.type.ts
🚧 Files skipped from review as they are similar to previous changes (8)
- docs/demos/bubble/markdown.vue
- packages/components/src/index.ts
- packages/components/src/bubble/index.ts
- packages/components/src/bubble/BubbleProvider.vue
- docs/demos/bubble/messages.vue
- docs/demos/bubble/schema-render.vue
- docs/demos/bubble/streaming.vue
- packages/components/src/bubble/index.type.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/bubble/renderers/tool.vue (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/bubble/renderers/text.vue (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/bubble/Bubble.vue (3)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
Learnt from: gene9831
PR: #59
File: packages/components/src/suggestion-popover/index.vue:131-133
Timestamp: 2025-05-27T03:35:11.008Z
Learning: In the SuggestionPopover component (packages/components/src/suggestion-popover/index.vue), the click handler can be bound unconditionally because the show computed property has a custom setter that prevents state mutations when props.trigger === 'manual'. This design centralizes trigger mode logic in the computed property rather than requiring conditional checks in event handlers.
Learnt from: SonyLeo
PR: #119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/bubble/renderers/markdown.ts (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/bubble/components/ContentItem.vue (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/bubble.md (1)
Learnt from: gene9831
PR: #123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
🪛 LanguageTool
docs/src/components/bubble.md
[uncategorized] ~84-~84: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:高"地"渲染
Context: ...ContentClassRenderer` 类 类渲染器通常用来复用复杂度较高的渲染器,比如MarkdownIt实例 ```typescript class ...
(wb4)
🔇 Additional comments (5)
packages/components/src/bubble/renderers/text.vue (1)
1-17: LGTM! Clean and well-structured text renderer.The component follows Vue 3 best practices with proper TypeScript typing, appropriate CSS styling for text content, and good whitespace handling with
pre-line.packages/components/src/bubble/renderers/markdown.ts (1)
28-35: LGTM! Good security practices with DOMPurify integration.The sanitization logic properly handles both enabled and disabled states, includes appropriate warnings for disabled sanitization, and correctly uses Vue's
h()function for VNode creation.packages/components/src/bubble/renderers/tool.vue (1)
85-181: LGTM! Well-structured component with good styling and UX.The component provides excellent visual feedback with status-specific styling, smooth animations, and a clean expandable interface. The CSS organization with both scoped styles and CSS modules is appropriate.
packages/components/src/bubble/Bubble.vue (2)
76-80: LGTM! Clean integration with the new ContentItem system.The array content rendering using ContentItem provides excellent flexibility for multi-format message support, and the conditional rendering logic is well-structured.
111-122: Good implementation of shape-based border radius styling.The conditional CSS classes for corner shapes provide clean visual differentiation based on placement, improving the overall bubble design system.
| const getVNodeOrComponent = (type: string) => { | ||
| const options = { ...attrs, ...props } | ||
|
|
||
| const renderer = rendererMap.get(type) || fallbackRenderer | ||
|
|
||
| if (typeof renderer === 'function') { | ||
| const renderFn = renderer as BubbleContentFunctionRenderer | ||
| return { isComponent: false, vNodeOrComponent: renderFn(options) } | ||
| } | ||
|
|
||
| if (renderer instanceof BubbleContentClassRenderer) { | ||
| return { isComponent: false, vNodeOrComponent: renderer.render(options) } | ||
| } | ||
|
|
||
| return { isComponent: true, vNodeOrComponent: renderer } | ||
| } | ||
|
|
||
| const { isComponent, vNodeOrComponent } = getVNodeOrComponent(props.type) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling and improve type safety for renderer resolution.
The renderer resolution logic lacks error handling and robust type guards, which could lead to runtime issues if renderers fail or invalid types are provided.
Apply this diff to improve robustness:
const getVNodeOrComponent = (type: string) => {
const options = { ...attrs, ...props }
const renderer = rendererMap.get(type) || fallbackRenderer
if (typeof renderer === 'function') {
const renderFn = renderer as BubbleContentFunctionRenderer
- return { isComponent: false, vNodeOrComponent: renderFn(options) }
+ try {
+ return { isComponent: false, vNodeOrComponent: renderFn(options) }
+ } catch (error) {
+ console.error('Error in content function renderer:', error)
+ return { isComponent: true, vNodeOrComponent: fallbackRenderer }
+ }
}
if (renderer instanceof BubbleContentClassRenderer) {
- return { isComponent: false, vNodeOrComponent: renderer.render(options) }
+ try {
+ return { isComponent: false, vNodeOrComponent: renderer.render(options) }
+ } catch (error) {
+ console.error('Error in content class renderer:', error)
+ return { isComponent: true, vNodeOrComponent: fallbackRenderer }
+ }
}
+ // Ensure it's a valid Vue component
+ if (typeof renderer === 'object' && renderer !== null) {
return { isComponent: true, vNodeOrComponent: renderer }
+ }
+
+ console.warn('Invalid renderer type provided:', renderer)
+ return { isComponent: true, vNodeOrComponent: fallbackRenderer }
}🤖 Prompt for AI Agents
In packages/components/src/bubble/components/ContentItem.vue around lines 22 to
39, the function getVNodeOrComponent lacks error handling and strong type checks
for the renderer resolution. To fix this, add explicit type guards to verify the
renderer's type before usage, and wrap the renderer invocation in try-catch
blocks to handle potential runtime errors gracefully. Also, add a fallback error
or warning if the renderer is invalid or missing to improve robustness and
prevent silent failures.
| const textAndIconMap = new Map<string, { text: string; icon: Component }>([ | ||
| ['running', { text: '正在调用', icon: IconLoading }], | ||
| ['success', { text: '已调用', icon: IconPlugin }], | ||
| ['failed', { text: '调用失败', icon: IconError }], | ||
| ['cancelled', { text: '已取消', icon: IconCancelled }], | ||
| ]) | ||
|
|
||
| const textAndIcon = computed(() => { | ||
| return textAndIconMap.get(props.status) || { text: '', icon: IconPlugin } | ||
| }) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add internationalization support for status messages.
The status messages are hardcoded in Chinese, which limits the component's reusability in international applications.
Consider accepting status text as props or using an i18n solution:
const props = defineProps<{
name: string
status: 'running' | 'success' | 'failed' | 'cancelled'
content?: string | { params?: object; result?: object; [x: string]: unknown }
formatPretty?: boolean
+ statusTexts?: Record<string, string>
}>()
+const defaultStatusTexts = {
+ running: '正在调用',
+ success: '已调用',
+ failed: '调用失败',
+ cancelled: '已取消'
+}
const textAndIconMap = new Map<string, { text: string; icon: Component }>([
- ['running', { text: '正在调用', icon: IconLoading }],
- ['success', { text: '已调用', icon: IconPlugin }],
- ['failed', { text: '调用失败', icon: IconError }],
- ['cancelled', { text: '已取消', icon: IconCancelled }],
+ ['running', { text: (props.statusTexts || defaultStatusTexts).running, icon: IconLoading }],
+ ['success', { text: (props.statusTexts || defaultStatusTexts).success, icon: IconPlugin }],
+ ['failed', { text: (props.statusTexts || defaultStatusTexts).failed, icon: IconError }],
+ ['cancelled', { text: (props.statusTexts || defaultStatusTexts).cancelled, icon: IconCancelled }],
])📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const textAndIconMap = new Map<string, { text: string; icon: Component }>([ | |
| ['running', { text: '正在调用', icon: IconLoading }], | |
| ['success', { text: '已调用', icon: IconPlugin }], | |
| ['failed', { text: '调用失败', icon: IconError }], | |
| ['cancelled', { text: '已取消', icon: IconCancelled }], | |
| ]) | |
| const textAndIcon = computed(() => { | |
| return textAndIconMap.get(props.status) || { text: '', icon: IconPlugin } | |
| }) | |
| // Add a new optional prop for overriding status texts | |
| const props = defineProps<{ | |
| name: string | |
| status: 'running' | 'success' | 'failed' | 'cancelled' | |
| content?: string | { params?: object; result?: object; [x: string]: unknown } | |
| formatPretty?: boolean | |
| statusTexts?: Record<string, string> | |
| }>() | |
| // Default Chinese texts, can be overridden via props.statusTexts | |
| const defaultStatusTexts: Record<string, string> = { | |
| running: '正在调用', | |
| success: '已调用', | |
| failed: '调用失败', | |
| cancelled: '已取消' | |
| } | |
| const textAndIconMap = new Map<string, { text: string; icon: Component }>([ | |
| [ | |
| 'running', | |
| { | |
| text: (props.statusTexts || defaultStatusTexts).running, | |
| icon: IconLoading | |
| } | |
| ], | |
| [ | |
| 'success', | |
| { | |
| text: (props.statusTexts || defaultStatusTexts).success, | |
| icon: IconPlugin | |
| } | |
| ], | |
| [ | |
| 'failed', | |
| { | |
| text: (props.statusTexts || defaultStatusTexts).failed, | |
| icon: IconError | |
| } | |
| ], | |
| [ | |
| 'cancelled', | |
| { | |
| text: (props.statusTexts || defaultStatusTexts).cancelled, | |
| icon: IconCancelled | |
| } | |
| ] | |
| ]) | |
| const textAndIcon = computed(() => { | |
| return textAndIconMap.get(props.status) || { text: '', icon: IconPlugin } | |
| }) |
🤖 Prompt for AI Agents
In packages/components/src/bubble/renderers/tool.vue around lines 14 to 23, the
status messages are hardcoded in Chinese, limiting internationalization. To fix
this, replace the hardcoded text with calls to an i18n translation function or
accept the status text as props. This will allow the component to support
multiple languages and improve reusability in international applications.
| const highlightJSON = <T extends string | object>(json?: T): string => { | ||
| if (!json) { | ||
| return '' | ||
| } | ||
|
|
||
| let prettyJson = '' | ||
| const space = props.formatPretty ? 2 : 0 | ||
|
|
||
| try { | ||
| if (typeof json === 'string') { | ||
| prettyJson = JSON.stringify(JSON.parse(json), null, space) | ||
| } else { | ||
| prettyJson = JSON.stringify(json, null, space) | ||
| } | ||
| } catch (error) { | ||
| console.warn(error) | ||
| } | ||
|
|
||
| prettyJson = prettyJson.replace( | ||
| /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, | ||
| (match) => { | ||
| let className = 'number' | ||
| if (/^"/.test(match)) { | ||
| className = /:$/.test(match) ? 'key' : 'string' | ||
| } else if (/true|false/.test(match)) { | ||
| className = 'boolean' | ||
| } else if (/null/.test(match)) { | ||
| className = 'null' | ||
| } | ||
| return `<span class="${classes[className]}">${match}</span>` | ||
| }, | ||
| ) | ||
|
|
||
| return prettyJson | ||
| } |
There was a problem hiding this comment.
Add sanitization for JSON content before HTML rendering.
The highlightJSON function creates HTML with user-provided content that gets rendered via v-html, which poses an XSS risk if the content contains malicious scripts.
Apply this diff to sanitize the content:
+import DOMPurify from 'dompurify'
const highlightJSON = <T extends string | object>(json?: T): string => {
if (!json) {
return ''
}
let prettyJson = ''
const space = props.formatPretty ? 2 : 0
try {
if (typeof json === 'string') {
prettyJson = JSON.stringify(JSON.parse(json), null, space)
} else {
prettyJson = JSON.stringify(json, null, space)
}
} catch (error) {
console.warn(error)
+ return DOMPurify.sanitize(String(json))
}
prettyJson = prettyJson.replace(
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
(match) => {
let className = 'number'
if (/^"/.test(match)) {
className = /:$/.test(match) ? 'key' : 'string'
} else if (/true|false/.test(match)) {
className = 'boolean'
} else if (/null/.test(match)) {
className = 'null'
}
return `<span class="${classes[className]}">${match}</span>`
},
)
- return prettyJson
+ return DOMPurify.sanitize(prettyJson)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const highlightJSON = <T extends string | object>(json?: T): string => { | |
| if (!json) { | |
| return '' | |
| } | |
| let prettyJson = '' | |
| const space = props.formatPretty ? 2 : 0 | |
| try { | |
| if (typeof json === 'string') { | |
| prettyJson = JSON.stringify(JSON.parse(json), null, space) | |
| } else { | |
| prettyJson = JSON.stringify(json, null, space) | |
| } | |
| } catch (error) { | |
| console.warn(error) | |
| } | |
| prettyJson = prettyJson.replace( | |
| /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, | |
| (match) => { | |
| let className = 'number' | |
| if (/^"/.test(match)) { | |
| className = /:$/.test(match) ? 'key' : 'string' | |
| } else if (/true|false/.test(match)) { | |
| className = 'boolean' | |
| } else if (/null/.test(match)) { | |
| className = 'null' | |
| } | |
| return `<span class="${classes[className]}">${match}</span>` | |
| }, | |
| ) | |
| return prettyJson | |
| } | |
| import DOMPurify from 'dompurify' | |
| const highlightJSON = <T extends string | object>(json?: T): string => { | |
| if (!json) { | |
| return '' | |
| } | |
| let prettyJson = '' | |
| const space = props.formatPretty ? 2 : 0 | |
| try { | |
| if (typeof json === 'string') { | |
| prettyJson = JSON.stringify(JSON.parse(json), null, space) | |
| } else { | |
| prettyJson = JSON.stringify(json, null, space) | |
| } | |
| } catch (error) { | |
| console.warn(error) | |
| return DOMPurify.sanitize(String(json)) | |
| } | |
| prettyJson = prettyJson.replace( | |
| /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, | |
| (match) => { | |
| let className = 'number' | |
| if (/^"/.test(match)) { | |
| className = /:$/.test(match) ? 'key' : 'string' | |
| } else if (/true|false/.test(match)) { | |
| className = 'boolean' | |
| } else if (/null/.test(match)) { | |
| className = 'null' | |
| } | |
| return `<span class="${classes[className]}">${match}</span>` | |
| }, | |
| ) | |
| return DOMPurify.sanitize(prettyJson) | |
| } |
🤖 Prompt for AI Agents
In packages/components/src/bubble/renderers/tool.vue between lines 27 and 61,
the highlightJSON function returns HTML with user content that is directly
rendered via v-html, creating an XSS vulnerability. To fix this, sanitize the
JSON string output before applying the regex replacement and returning it. Use a
proper HTML escaping method to replace characters like <, >, &, ", and ' with
their HTML entities to prevent script injection while preserving the JSON
formatting and syntax highlighting.
Important
此PR包含破坏性变更
TODO
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Style
Chores