fix: template editor cursor position and style#165
fix: template editor cursor position and style#165gene9831 merged 8 commits intoopentiny:developfrom
Conversation
WalkthroughThe changes implement dynamic class binding for compact styling in the chat input footer, update icon sizing to use CSS variables, introduce centralized state management and browser-specific handling for zero-width space characters in the template editor, add special deletion handling for sentinel nodes, and add logic to exit template mode when the template is empty. These updates enhance consistency and flexibility in UI behavior and state handling. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant TemplateEditor
participant Sender
participant UI
User->>TemplateEditor: Edit template content
TemplateEditor->>TemplateEditor: setOriginalData(items)
TemplateEditor->>TemplateEditor: handleSentinelNodeForwardDeletion(selectedItems, range, inputType)
TemplateEditor->>Sender: Emit updated template data
Sender->>Sender: handleTemplateUpdate(data)
alt Data is empty or only zero-width space
Sender->>Sender: exitTemplateMode()
Sender->>UI: Exit template editing mode
else Data is valid
Sender->>UI: Update template content
end
Estimated code review effort3 (30–60 minutes) Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
packages/components/src/sender/components/TemplateEditor.vueOops! Something went wrong! :( ESLint: 9.31.0 Error: The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/components/src/sender/components/TemplateEditor.vue (1)
74-101: Document the browser-specific zero-width space handling logic.The browser-specific cleanup logic for zero-width spaces is non-obvious and should be documented to explain why Safari requires cleanup at the end while Chrome requires it at the beginning. This will help future maintainers understand the rationale.
Add comments explaining the browser differences:
const setOriginalData = (items: (TextItem | TemplateItem)[]) => { if (items.length > 0) { if (items[0].type === 'template') { + // Prepend a zero-width space text node as a sentinel to ensure templates are never first originalData.value = [{ type: 'text', content: '\u200B', id: randomId() } as TextItem | TemplateItem].concat( items, ) } else { originalData.value = items const firstItem = items[0] const lastItem = items[items.length - 1] if (isSafariBrowser) { - // 在 safari 环境下,如果最后一个元素是 text,清空 content + // 在 safari 环境下,如果最后一个元素是 text,清空 content + // Safari handles cursor positioning differently at the end of content if (lastItem.content !== '\u200B') { lastItem.content = lastItem.content.replace(/\u200B/g, '') } } else { - // 在 chrome 环境下,如果第一个元素是 text,清空 content + // 在 chrome 环境下,如果第一个元素是 text,清空 content + // Chrome requires cleanup at the beginning for proper cursor behavior if (firstItem.content !== '\u200B') { firstItem.content = firstItem.content.replace(/\u200B/g, '') } } } } else { originalData.value = items } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
docs/demos/examples/Assistant.vue(1 hunks)packages/components/src/sender/components/ActionButtons.vue(1 hunks)packages/components/src/sender/components/TemplateEditor.vue(8 hunks)packages/components/src/sender/index.vue(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 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/sender/index.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/sender/components/ActionButtons.vue (2)
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变量。
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/demos/examples/Assistant.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.
🔇 Additional comments (5)
docs/demos/examples/Assistant.vue (1)
58-58: LGTM!The conditional class binding correctly applies compact styling when not in fullscreen mode, enhancing the UI responsiveness.
packages/components/src/sender/components/ActionButtons.vue (1)
257-258: LGTM!Good refactoring to use CSS variables for icon sizing, which improves maintainability and enables responsive styling through the compact mode overrides.
packages/components/src/sender/index.vue (1)
279-287: Good UX improvement for automatic template mode exit.The logic correctly identifies empty templates, including the edge case of a single zero-width space character which serves as a sentinel node. This enhances the user experience by automatically transitioning back to normal input mode when the template is cleared.
packages/components/src/sender/components/TemplateEditor.vue (2)
420-458: Well-implemented sentinel node protection.The function correctly prevents deletion of zero-width space sentinel nodes by redirecting the cursor to the next element. This maintains the structural integrity of the template editor while providing a seamless user experience.
191-191: Excellent refactoring to centralize state updates.All direct assignments to
originalData.valuehave been consistently replaced withsetOriginalData()calls, ensuring uniform application of browser-specific handling and sentinel node management throughout the component.Also applies to: 290-301, 511-521, 834-834
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/components/src/sender/components/TemplateEditor.vue (1)
927-931: Address global styling conflicts with.tr-sender-compactclass.This change creates the same global styling issue previously identified. The
.tr-sender-compactclass is defined in multiple components, and removingscopedwill cause these styles to become global and potentially override each other.Consider these solutions:
- Use a more specific selector:
.template-editor .tr-sender-compact { ... }- Use a unique class name:
.tr-template-editor-compact { ... }- Re-add scoped attribute and handle compact styling through props/computed classes
The current approach may work for the demo but creates maintenance issues and unpredictable styling behavior in larger applications.
🧹 Nitpick comments (2)
packages/components/src/sender/components/TemplateEditor.vue (2)
74-100: Good centralization of state management logic.The
setOriginalDatafunction effectively centralizes the complex logic for handling zero-width space characters and browser-specific behavior. However, consider these improvements:+/** + * Centralized function to update originalData with browser-specific zero-width space handling + * @param items - Array of text or template items to set as the new data + */ const setOriginalData = (items: (TextItem | TemplateItem)[]) => {Note: The function mutates the input
itemsarray by modifying thecontentproperty of items. Consider cloning items if immutability is important elsewhere in the codebase.
511-520: Consider combining consecutivesetOriginalDatacalls for efficiency.The two consecutive calls to
setOriginalDatacould be combined into a single operation to avoid unnecessary processing:- // 删除空数据 - setOriginalData(originalData.value.filter((item) => !toDeleted.includes(item.id))) - setOriginalData( - originalData.value.filter((item) => { - if (item.type === 'text') { - return item.content.length > 0 - } - const templateItem = item as TemplateItem - return [templateItem.prefix, templateItem.suffix, templateItem.content].join('').length > 0 - }), - ) + // 删除空数据 + setOriginalData( + originalData.value.filter((item) => { + // First filter: exclude items marked for deletion + if (toDeleted.includes(item.id)) { + return false + } + // Second filter: exclude empty items + if (item.type === 'text') { + return item.content.length > 0 + } + const templateItem = item as TemplateItem + return [templateItem.prefix, templateItem.suffix, templateItem.content].join('').length > 0 + }), + )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/components/src/sender/components/TemplateEditor.vue(7 hunks)
🧰 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.
packages/components/src/sender/components/TemplateEditor.vue (1)
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变量。
在 紧凑模式(tr-sender-compact)下,文字大小为 14px; 全屏模式下,文字大小为 16px
Summary by CodeRabbit
New Features
Style
Bug Fixes