feat(bubble): add provider-level box and content attributes - #332
Conversation
WalkthroughAdds provider-level attribute injection to Bubble components: Changes
Sequence Diagram(s)sequenceDiagram
participant App as App / User
participant Provider as TrBubbleProvider
participant Composable as useBubble*Renderer
participant Config as box/contentAttributes
participant Wrapper as BubbleBox/ContentWrapper
participant Renderer as Selected Renderer
App->>Provider: Mount with boxAttributes/contentAttributes
Provider->>Composable: setupBubbleBoxRenderer(..., boxAttributes) / setupBubbleContentRenderer(..., contentAttributes)
Composable->>Config: provide injection key with config
Wrapper->>Composable: compute renderer for message/content
Composable->>Config: inject and resolve attributes (call fn or use object)
Config-->>Composable: resolved attributes
Composable->>Composable: merge provider attrs with match.attrs
Composable-->>Wrapper: return { renderer, attributes }
Wrapper->>Renderer: instantiate renderer.renderer with merged props & v-bind="attributes"
Renderer->>Renderer: render DOM including injected data-*
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/src/components/bubble.md (1)
213-226: Consider documenting the Box wrapper's reserved attributes.This section describes provider↔match merge precedence, but does not mention that the Box wrapper still applies its own
data-role/data-placement/data-shapeon top of the merged attributes (see related note onBubbleBoxWrapper.vue). If that override is intentional, a one-line callout here would prevent surprises for users who try to set those specific keys viabox-attributes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/src/components/bubble.md` around lines 213 - 226, Add a one-line callout under the BubbleProvider attributes section clarifying that the Box wrapper applies reserved attributes and may override provider/match attributes: explicitly mention BubbleBoxWrapper.vue reserves data-role, data-placement, and data-shape on the Box and that those keys set by box-attributes or match-level attributes will be overridden by the wrapper's values; reference BubbleProvider, box-attributes, content-attributes and BubbleBoxWrapper.vue so readers know where the behavior originates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/components/src/bubble/BubbleBoxWrapper.vue`:
- Around line 16-22: The wrapper currently binds renderer.attributes before the
explicit :data-role/:data-placement/:data-shape props, causing
props.role/placement/shape to always override any same-named entries in
renderer.attributes; either revert the order (move v-bind="renderer.attributes"
after the explicit bindings) so provider/match-supplied attributes win, or if
the override is intentional, document that BubbleBoxWrapper reserves these
structural attributes (mention renderer.renderer, renderer.attributes, and
props.role/props.placement/props.shape) and update docs to describe the
merge/precedence rule.
---
Nitpick comments:
In `@docs/src/components/bubble.md`:
- Around line 213-226: Add a one-line callout under the BubbleProvider
attributes section clarifying that the Box wrapper applies reserved attributes
and may override provider/match attributes: explicitly mention
BubbleBoxWrapper.vue reserves data-role, data-placement, and data-shape on the
Box and that those keys set by box-attributes or match-level attributes will be
overridden by the wrapper's values; reference BubbleProvider, box-attributes,
content-attributes and BubbleBoxWrapper.vue so readers know where the behavior
originates.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 84913a30-f025-449e-a095-efabec5f30c5
📒 Files selected for processing (9)
docs/demos/bubble/provider-attributes.vuedocs/src/components/bubble.mdpackages/components/src/bubble/BubbleBoxWrapper.vuepackages/components/src/bubble/BubbleContentWrapper.vuepackages/components/src/bubble/BubbleProvider.vuepackages/components/src/bubble/composables/useBubbleBoxRenderer.tspackages/components/src/bubble/composables/useBubbleContentRenderer.tspackages/components/src/bubble/constants.tspackages/components/src/bubble/index.type.ts
📦 Package Previewpnpm add https://pkg.pr.new/@opentiny/tiny-robot@9db5c68 pnpm add https://pkg.pr.new/@opentiny/tiny-robot-kit@9db5c68 pnpm add https://pkg.pr.new/@opentiny/tiny-robot-svgs@9db5c68 commit: 9db5c68 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
docs/demos/bubble/custom-composite-renderer.vue (2)
25-32: Minor:findmatches emptyreasoning_contentstrings.
typeof message.reasoning_content === 'string'istruefor"", which would route an empty reasoning string intoRecursiveReasoningRendererand render an empty "自定义推理过程" section. The resolver below already guards with truthiness — for consistency, thefindpredicate could do the same:♻️ Suggested tweak
- find: (message) => typeof message.reasoning_content === 'string', + find: (message) => typeof message.reasoning_content === 'string' && message.reasoning_content.length > 0,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/demos/bubble/custom-composite-renderer.vue` around lines 25 - 32, The find predicate in the contentRendererMatches entry currently matches empty strings because it uses typeof message.reasoning_content === 'string'; update the predicate used for the BubbleContentRendererMatch to require a non-empty reasoning string (e.g., check truthiness or length of message.reasoning_content) so only messages with actual reasoning content route to the renderer; adjust the match for the RecursiveReasoningRenderer entry (the find function referencing message.reasoning_content) to mirror the resolver's guard.
34-43: Optional: consider a non-data-rolekey in this demo to avoid muddying the reserved-attribute story.
data-roleis reserved onBubbleBoxWrapper(always derived fromprops.role), so a reader who copy-pastes this demo and switchescontentAttributes→boxAttributeswill silently lose their injecteddata-role. A demo key likedata-demo-role(parallel todata-demo-kindalready used here) would teach the safer convention up front.♻️ Suggested tweak
return { 'data-demo-kind': isReasoning ? 'reasoning' : 'content', - 'data-role': message.role || 'assistant', + 'data-demo-role': message.role || 'assistant', 'data-content-type': content.type, 'data-content-index': contentIndex, }Based on learnings:
data-role,data-placement, anddata-shapeare reserved structural attributes on BubbleBoxWrapper and override same-named keys from provider/match attributes; users injecting custom attributes should prefer non-reserved keys.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/demos/bubble/custom-composite-renderer.vue` around lines 34 - 43, The demo uses a reserved attribute key `data-role` inside the contentAttributes function (contentAttributes) which can conflict with BubbleBoxWrapper's reserved props; change the injected key to a non-reserved name (e.g., `data-demo-role`) so demo users don’t accidentally override structural attributes—update the returned object in contentAttributes to replace 'data-role' with 'data-demo-role' (keeping the same value expression message.role || 'assistant') and keep the other demo-specific keys like 'data-demo-kind' to teach the safer convention.docs/src/components/bubble.md (1)
336-336: Minor doc nit: clarify what "重复id" means in the multi-root warning.The bullet warns against copying the same attributes onto multiple sibling nodes (duplicate
id, ARIA, test selectors). In practice, provider/match attributes are usuallydata-*, where duplication is harmless; the only common hazards areid,aria-labelledby/aria-describedbyreferences, anddata-testid. Listing those explicitly would make the warning more actionable, but content is otherwise accurate.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/src/components/bubble.md` at line 336, Update the multi-root renderer warning sentence (the line mentioning `inheritAttrs: false` and `$attrs`) to explicitly list the attribute types that are hazardous when duplicated across sibling nodes: `id`, ARIA reference attributes like `aria-labelledby` and `aria-describedby`, and test selectors such as `data-testid`; note that generic `data-*` attributes are usually harmless. Keep the guidance to use `inheritAttrs: false` and explicitly bind `$attrs` to a single node to avoid those specific duplicates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@docs/demos/bubble/custom-composite-renderer.vue`:
- Around line 25-32: The find predicate in the contentRendererMatches entry
currently matches empty strings because it uses typeof message.reasoning_content
=== 'string'; update the predicate used for the BubbleContentRendererMatch to
require a non-empty reasoning string (e.g., check truthiness or length of
message.reasoning_content) so only messages with actual reasoning content route
to the renderer; adjust the match for the RecursiveReasoningRenderer entry (the
find function referencing message.reasoning_content) to mirror the resolver's
guard.
- Around line 34-43: The demo uses a reserved attribute key `data-role` inside
the contentAttributes function (contentAttributes) which can conflict with
BubbleBoxWrapper's reserved props; change the injected key to a non-reserved
name (e.g., `data-demo-role`) so demo users don’t accidentally override
structural attributes—update the returned object in contentAttributes to replace
'data-role' with 'data-demo-role' (keeping the same value expression
message.role || 'assistant') and keep the other demo-specific keys like
'data-demo-kind' to teach the safer convention.
In `@docs/src/components/bubble.md`:
- Line 336: Update the multi-root renderer warning sentence (the line mentioning
`inheritAttrs: false` and `$attrs`) to explicitly list the attribute types that
are hazardous when duplicated across sibling nodes: `id`, ARIA reference
attributes like `aria-labelledby` and `aria-describedby`, and test selectors
such as `data-testid`; note that generic `data-*` attributes are usually
harmless. Keep the guidance to use `inheritAttrs: false` and explicitly bind
`$attrs` to a single node to avoid those specific duplicates.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 08d62a71-14eb-4c84-a98e-94f8a81bdaf3
📒 Files selected for processing (5)
docs/demos/bubble/RecursiveReasoningRenderer.vuedocs/demos/bubble/custom-composite-renderer.vuedocs/src/components/bubble.mdpackages/components/src/bubble/renderers/Reasoning.vuepackages/components/src/bubble/renderers/Tools.vue
🧹 Preview Cleaned UpThe preview deployment has been removed. |

背景
当前如果想给所有 Box / Content 统一注入 attributes,只依赖 renderer match 不够稳定,因为无法保证所有消息都会匹配到自定义 renderer。
改动
BubbleProvider新增boxAttributes和contentAttributesBox/Content两条渲染链路统一支持 attributes 合并useBubbleContentRenderer的 attributes 返回能力验证
docs/demos/bubble/provider-attributes.vueSummary by CodeRabbit
New Features
Documentation