Skip to content

feat: Bubble support various message formats#123

Merged
hexqi merged 28 commits intoopentiny:developfrom
gene9831:feat/bubble-inference
Jul 26, 2025
Merged

feat: Bubble support various message formats#123
hexqi merged 28 commits intoopentiny:developfrom
gene9831:feat/bubble-inference

Conversation

@gene9831
Copy link
Copy Markdown
Collaborator

@gene9831 gene9831 commented Jun 20, 2025

Important

此PR包含破坏性变更

  • 单个气泡支持多格式消息,可以自定义渲染器来渲染不同格式消息
  • 气泡支持设置形状
  • 气泡列表支持设置加载中状态

TODO

  • 多格式消息如何转换成可复制的字符串
  • 多格式组件消息如何使用代码主动交互

Summary by CodeRabbit

  • New Features

    • Introduced a flexible message rendering system for bubbles, supporting custom, class-based, and component renderers.
    • Added support for multiple message formats, including text, markdown, tool status, and collapsible content.
    • Implemented a BubbleProvider component for registering and managing message renderers.
    • Enabled role-based avatars, shape customization (rounded/corner), and improved loading indicators in bubble lists.
  • Bug Fixes

    • Improved reactivity and conditional rendering in bubble lists.
  • Documentation

    • Expanded and clarified documentation on bubble shapes, loading states, multiple message formats, and API types.
  • Style

    • Updated and added styles for new bubble shapes, loading indicators, and syntax highlighting.
  • Chores

    • Added "dompurify" as a dependency for secure HTML sanitization.

gene9831 added 17 commits May 9, 2025 16:49
…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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 20, 2025

Walkthrough

This 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

File(s) Change Summary
docs/demos/bubble/loading.vue, docs/demos/bubble/markdown.vue, docs/demos/bubble/messages.vue, docs/demos/bubble/schema-render.vue, docs/demos/bubble/shape.vue, docs/demos/bubble/slots.vue, docs/demos/bubble/streaming.vue Updated and added demos to showcase new bubble features, message formats, explicit renderer usage, and shape/slot variations.
docs/src/components/bubble.md Extended documentation: added sections for bubble shape, loading, modular renderers, and updated API/type definitions.
packages/components/package.json Added dompurify dependency.
packages/components/src/bubble/Bubble.vue Refactored to support modular content rendering via contentRenderer, removed markdown-it, updated loading and shape logic, and improved slot/class handling.
packages/components/src/bubble/BubbleList.vue Added support for list-level loading bubble, deep watch on items, and hidden item filtering.
packages/components/src/bubble/BubbleProvider.vue New provider component for message renderers, using Vue's provide/inject.
packages/components/src/bubble/index.ts Exported new provider and renderer classes, registered provider for global install.
packages/components/src/bubble/index.type.ts Refactored and modularized type definitions; added interfaces for renderer system and loading support.
packages/components/src/index.ts Registered and exported new bubble provider and renderer classes.
packages/components/src/container/index.vue Changed CSS selector for header sibling styling.
packages/components/src/styles/animations.css, packages/components/src/styles/root.css Added spin animation and imported it into root CSS.
packages/components/src/bubble/components/ContentItem.vue New component for dynamic content item rendering based on type and renderer map.
packages/components/src/bubble/components/index.ts Exported ContentItem component.
packages/components/src/bubble/renderers/class-renderer.ts Added abstract class for class-based content renderers.
packages/components/src/bubble/renderers/collapsible-text.ts, packages/components/src/bubble/renderers/collapsible-text.vue Added collapsible text renderer and its Vue component.
packages/components/src/bubble/renderers/defaultRendererMap.ts Added default renderer map and provider keys for DI.
packages/components/src/bubble/renderers/index.ts Aggregated exports for all renderer modules.
packages/components/src/bubble/renderers/index.type.ts Added types for content renderers and content items.
packages/components/src/bubble/renderers/markdown.ts Added class-based markdown renderer with sanitization.
packages/components/src/bubble/renderers/text.ts, packages/components/src/bubble/renderers/text.vue Added text content renderer and its Vue component.
packages/components/src/bubble/renderers/tool.ts, packages/components/src/bubble/renderers/tool.vue Added tool content renderer and its Vue component.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
In the meadow of bubbles, new shapes take the stage,
With renderers and markdown, we hop to a new age.
Messages collapse, tools spin with delight,
Avatars and roles now shimmer just right.
With a sprinkle of CSS and types shining through—
Hooray for the code, and carrots for you!

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

docs/demos/bubble/markdown.vue

Oops! Something went wrong! :(

ESLint: 9.32.0

Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.
at /node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:145:10
at async loadTypeScriptConfigFileWithJiti (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:144:3)
at async loadConfigFile (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:266:11)
at async ConfigLoader.calculateConfigArray (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:589:23)
at async #calculateConfigArray (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:743:23)
at async /node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/eslint/eslint.js:767:6
at async Promise.all (index 0)
at async ESLint.lintFiles (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/eslint/eslint.js:764:19)
at async Object.execute (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/cli.js:632:14)
at async main (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/bin/eslint.js:175:19)

docs/demos/bubble/schema-render.vue

Oops! Something went wrong! :(

ESLint: 9.32.0

Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.
at /node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:145:10
at async loadTypeScriptConfigFileWithJiti (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:144:3)
at async loadConfigFile (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:266:11)
at async ConfigLoader.calculateConfigArray (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:589:23)
at async #calculateConfigArray (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:743:23)
at async /node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/eslint/eslint.js:767:6
at async Promise.all (index 0)
at async ESLint.lintFiles (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/eslint/eslint.js:764:19)
at async Object.execute (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/cli.js:632:14)
at async main (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/bin/eslint.js:175:19)

docs/demos/bubble/streaming.vue

Oops! Something went wrong! :(

ESLint: 9.32.0

Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.
at /node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:145:10
at async loadTypeScriptConfigFileWithJiti (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:144:3)
at async loadConfigFile (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:266:11)
at async ConfigLoader.calculateConfigArray (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:589:23)
at async #calculateConfigArray (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/config/config-loader.js:743:23)
at async /node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/eslint/eslint.js:767:6
at async Promise.all (index 0)
at async ESLint.lintFiles (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/eslint/eslint.js:764:19)
at async Object.execute (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/lib/cli.js:632:14)
at async main (/node_modules/.pnpm/eslint@9.32.0/node_modules/eslint/bin/eslint.js:175:19)

  • 19 others

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🔭 Outside diff range comments (1)
packages/components/src/bubble/Bubble.vue (1)

140-154: Fix inconsistent CSS class naming

Multiple CSS class names use tr-bubbule instead of tr-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 any type 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): VNode

This 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 collapsed is initialized to false, meaning the component starts in an expanded state. Consider renaming it to expanded or isExpanded for 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 tags

While 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-safe

The 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 logic

The 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 contentRenderer is 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 messages property 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9cf54e9 and d959ed1.

⛔ Files ignored due to path filters (2)
  • packages/svgs/src/assets/success.svg is excluded by !**/*.svg
  • packages/svgs/src/assets/tool-running.svg is 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-word and white-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-renderer prop 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 new content-renderer prop with an explicit BubbleMarkdownMessageRenderer instance. 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 withDefaults ensures 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 Map is 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 Tr prefixed 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 any in 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 BubbleCommonProps and the extension pattern used in BubbleProps and BubbleRoleConfig demonstrates 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/bash

Search 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 -->

Comment thread packages/components/src/bubble/message/collapse.vue
Comment thread packages/components/src/bubble/message/markdown.ts
Comment thread packages/components/src/bubble/message/markdown.ts
Comment thread packages/components/src/bubble/message/markdown.ts
Comment thread packages/components/src/bubble/message/tool.vue Outdated
Comment thread packages/components/src/bubble/message/Message.vue
Comment thread packages/components/src/bubble/message/Message.vue
Comment thread packages/components/src/bubble/Bubble.vue Outdated
Comment thread packages/components/src/bubble/Bubble.vue Outdated
- 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.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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-text to tr-bubble__body-text as 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

📥 Commits

Reviewing files that changed from the base of the PR and between d959ed1 and 65f07f8.

📒 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 shape prop 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.

Comment thread packages/components/src/bubble/Bubble.vue
… 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.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
docs/src/components/bubble.md (5)

23-28: Shape section looks good but can mention fallback behaviour

Nice addition — it’s immediately clear how to switch between rounded and corner.
Consider adding one sentence that clarifies what happens when an invalid value is passed (falls back to corner, throws, etc.) so users don’t have to look at the implementation.


59-69: Minor markdown formatting nit

The numbered list is rendered as “1. 1. 1. …”.
Prefix each item with 1. 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 explicit h import

h comes from vue. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 65f07f8 and 27f1986.

📒 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 job

The | in 'rounded' \| 'corner' is properly escaped, preventing the table from breaking.
Nothing to change here.

Comment thread docs/src/components/bubble.md
…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.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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: 重复用字 & 名称缺失

  1. “直接可用的” 出现连写。
  2. 标题 “内置需要自行导入的渲染有” 缺少 “器”,建议同时调整语序。
-目前内置直接可用的的渲染器类型有
+目前内置即可直接使用的渲染器类型有-内置需要自行导入的渲染有
+内置但需手动导入的渲染器有

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

📥 Commits

Reviewing files that changed from the base of the PR and between 27f1986 and fd2176f.

📒 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)

Comment thread packages/components/src/bubble/message/tool.vue Outdated
gene9831 added 2 commits July 24, 2025 10:30
- 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.
gene9831 added 4 commits July 25, 2025 11:25
…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.
@gene9831 gene9831 marked this pull request as ready for review July 26, 2025 07:48
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/components/src/bubble/message/tool.vue (1)

14-23: Add internationalization support for status text

The 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-role vs loadingRole

The narrative uses kebab-case (loading-role), while the API table below and the TS typings define loadingRole (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:

  1. The complex regex pattern is hard to maintain and debug
  2. Error handling only logs to console without user feedback
  3. Consider using a dedicated syntax highlighting library for better reliability

Consider using a library like prismjs or highlight.js for 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

📥 Commits

Reviewing files that changed from the base of the PR and between fd2176f and 37c98b1.

⛔ Files ignored due to path filters (4)
  • packages/svgs/src/assets/cancelled.svg is excluded by !**/*.svg
  • packages/svgs/src/assets/error.svg is excluded by !**/*.svg
  • packages/svgs/src/assets/loading.svg is excluded by !**/*.svg
  • packages/svgs/src/assets/plugin.svg is 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-html directive is used to render highlighted JSON. While the content comes from the internal highlightJSON function which should be safe, ensure that any user-provided content in props.content is properly sanitized to prevent XSS attacks.

The current implementation should be safe since highlightJSON only 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.vue file 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.

Comment thread packages/components/src/container/index.vue
…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.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
docs/src/components/bubble.md (1)

33-34: Prop name inconsistency persists (loading-role vs loadingRole)

The narrative still uses kebab-case loading-role, but the API table and TS typings use camelCase loadingRole. This mismatch will confuse readers and break auto-completion examples.

🧹 Nitpick comments (1)
docs/src/components/bubble.md (1)

70-96: Import h in code snippet for completeness

The function-style renderer example calls h but doesn’t show where it comes from. Beginners may copy-paste and run into “h is not defined”. Consider prepending import { 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

📥 Commits

Reviewing files that changed from the base of the PR and between 37c98b1 and 6c5dd18.

📒 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: shape section is clear and matches implementation

The new “气泡形状” section accurately documents the shape prop, its options, and default value. No issues spotted.

hexqi
hexqi previously approved these changes Jul 26, 2025
…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.
@hexqi hexqi merged commit a0edb8a into opentiny:develop Jul 26, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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: use loadingRole instead of loading-role

The 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 list

Small wording issues slip in here:

  1. “的的” – duplicated token.
  2. Missing “器” after “渲染”.
  3. Better parallelism for the two-part list.
-目前内置直接可用的的渲染器类型有
+目前内置直接可用的渲染器类型有

-内置需要自行导入的渲染有
+需要手动导入的内置渲染器有
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c5dd18 and 1f715cc.

📒 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.

Comment on lines +22 to +39
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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ 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.

Comment on lines +14 to +23
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 }
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ 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.

Suggested change
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.

Comment on lines +27 to +61
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
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants