Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughA new autocomplete feature has been added to the Mail app in the create module. The new Changes
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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: 0
🧹 Nitpick comments (1)
apps/mail/components/create/editor-autocomplete.ts (1)
27-70: Clean implementation of suggestion matching logicThe suggestion matching function correctly handles empty inputs, incorporates personalized suggestions based on sender/user info, and properly filters and sorts the suggestions.
However, there's an unnecessary console.log statement that should be removed.
- console.log(name);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/mail/components/create/editor-autocomplete.ts(1 hunks)apps/mail/components/create/editor.tsx(4 hunks)apps/mail/components/create/ghost-text.css(1 hunks)apps/mail/components/mail/reply-composer.tsx(4 hunks)
🔇 Additional comments (9)
apps/mail/components/create/ghost-text.css (1)
1-39: Well-implemented shimmer effect with proper dark mode supportThe CSS implementation for the ghost text suggestion provides an elegant shimmer effect with proper dark mode support. The animation timing and gradient values create a subtle but noticeable effect that should enhance the user experience.
apps/mail/components/mail/reply-composer.tsx (3)
16-16: Good addition of session managementAdding the useSession hook is appropriate for retrieving user information needed by the autocomplete feature.
32-32: Correctly extracts session dataThe session data extraction follows best practices using object destructuring.
292-316: Well-structured integration of user data to the Editor componentThe component now correctly passes both the user's information (from session) and sender's information (from email data) to the Editor component, enabling personalized autocomplete suggestions.
apps/mail/components/create/editor.tsx (3)
47-47: Appropriate import of the new AutoComplete extensionThe AutoComplete import is correctly added to integrate with the existing editor functionality.
75-82: Well-defined interface extensionsThe EditorProps interface has been properly extended with optional properties that maintain backward compatibility.
501-532: Comprehensive autocomplete configurationThe AutoComplete extension is configured with a rich set of text suggestions, categorized into openers, closers, and custom phrases. The integration with user and sender information enables personalized suggestions.
apps/mail/components/create/editor-autocomplete.ts (2)
1-19: Well-structured interfaces and proper importsThe interfaces and imports are organized clearly, with appropriate typing for the extension options.
72-144: Robust ProseMirror plugin implementationThe plugin implementation correctly:
- Handles Tab key events to trigger suggestions
- Extracts current line text for context
- Prevents default Tab behavior when suggestions are available
- Creates decorations with the shimmer effect for visual feedback
The error handling is thorough, with appropriate checks before attempting to process suggestions.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
apps/mail/components/create/ghost-text.css (2)
7-18: Consider using CSS variables for colors and sizing.The suggestion panel styling looks good, but consider using CSS variables for colors, borders, and sizing values for improved maintainability and easier theming.
+:root { + --suggestion-bg: white; + --suggestion-border: #e0e0e0; + --suggestion-shadow: rgba(0, 0, 0, 0.1); + --suggestion-max-height: 200px; + --suggestion-min-width: 200px; +} .suggestion-panel { position: absolute; - background: white; - border: 1px solid #e0e0e0; + background: var(--suggestion-bg); + border: 1px solid var(--suggestion-border); border-radius: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - max-height: 200px; + box-shadow: 0 2px 8px var(--suggestion-shadow); + max-height: var(--suggestion-max-height); overflow-y: auto; z-index: 1000; margin-top: 4px; - min-width: 200px; + min-width: var(--suggestion-min-width); }
58-75: Add cross-browser scrollbar compatibility.The scrollbar styling looks great for WebKit browsers (Chrome, Safari), but consider adding Firefox and standard scrollbar properties for cross-browser compatibility.
/* Add scrollbar styling */ .suggestion-panel::-webkit-scrollbar { width: 8px; } .suggestion-panel::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } .suggestion-panel::-webkit-scrollbar-thumb { background: #888; border-radius: 4px; } .suggestion-panel::-webkit-scrollbar-thumb:hover { background: #555; } +/* Firefox scrollbar */ +.suggestion-panel { + scrollbar-width: thin; + scrollbar-color: #888 #f1f1f1; +}apps/mail/components/create/editor-autocomplete.ts (3)
175-184: Add event listeners and cleanup for suggestion items.The suggestion panel creates DOM elements but doesn't attach click event listeners. Also, there's no cleanup mechanism which could lead to memory leaks.
// Create suggestion panel const panel = document.createElement('div'); panel.className = 'suggestion-panel'; suggestions.forEach((suggestion, index) => { const item = document.createElement('div'); item.className = `suggestion-item ${index === selectedIndex ? 'selected' : ''}`; item.textContent = suggestion.text; + // Add click handler for mouse selection + item.addEventListener('click', () => { + const tr = state.tr.insertText(suggestion.text.slice(currentLine.length), pos); + view.dispatch(tr); + }); panel.appendChild(item); });
190-195: Add ARIA attributes for better accessibility.The ghost text suggestion should include ARIA attributes to ensure screen readers can properly interpret it.
const span = document.createElement('span'); span.textContent = remainingText; span.className = 'ghost-text-suggestion'; + span.setAttribute('aria-hidden', 'true'); + span.setAttribute('role', 'presentation'); return span;
144-156: Add support for Escape key to dismiss suggestions.The code handles Tab and Enter for selection, but doesn't provide a way to dismiss suggestions without selecting any.
// Handle selection - if (event.key === 'Tab' || event.key === 'Enter') { + if (event.key === 'Escape') { + event.preventDefault(); + // Clear suggestions by forcing a re-render + view.dispatch(view.state.tr); + return true; + } else if (event.key === 'Tab' || event.key === 'Enter') { event.preventDefault(); const suggestion = suggestions[selectedIndex]; if (!suggestion) return false; const remainingText = suggestion.text.slice(currentLine.length); if (!remainingText) return false; view.dispatch(view.state.tr.insertText(remainingText, pos)); selectedIndex = 0; return true; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/mail/components/create/editor-autocomplete.ts(1 hunks)apps/mail/components/create/editor.tsx(4 hunks)apps/mail/components/create/ghost-text.css(1 hunks)apps/mail/components/mail/reply-composer.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/mail/components/mail/reply-composer.tsx
- apps/mail/components/create/editor.tsx
🔇 Additional comments (3)
apps/mail/components/create/ghost-text.css (2)
1-5: Well-designed ghost text styling.The ghost text styling has good opacity, non-intrusive coloring, and correctly uses
pointer-events: noneto ensure it doesn't interfere with user interaction.
77-91: Excellent dark mode implementation.The dark mode styling with gradient effect is well-implemented and properly uses
-webkit-background-clipwith the standardbackground-clipproperty for compatibility.apps/mail/components/create/editor-autocomplete.ts (1)
29-48: Well-implemented fuzzy matching algorithm.The fuzzy matching function is efficiently implemented with appropriate scoring for exact matches, prefix matches, and character-by-character matching. The normalization of the score by pattern length is a good approach.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/mail/components/create/editor-autocomplete.ts (2)
149-152: Clarify complex boolean logic with parentheses.The mix of
&&and||operators without clear grouping makes the code hard to understand and potentially error-prone.Apply this fix:
return matchesCurrentText && !isAlreadyUsed && - (!isInMiddleOfEmail || !suggestion.includes('Hello') && !suggestion.includes('Hi') && !suggestion.includes('Dear')) && + (!isInMiddleOfEmail || !(suggestion.includes('Hello') || suggestion.includes('Hi') || suggestion.includes('Dear'))) && !isSimilarGreetingUsed;
38-40: Consider resetting usedSuggestions when appropriate.The
usedSuggestionsSet persists for the lifetime of the extension, which could lead to unexpected behavior if the editor is used for multiple emails.Consider adding a method to reset this Set when a new email is started or when the editor content is cleared. This would prevent suggestions from being inappropriately filtered out across different emails.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/mail/components/create/editor-autocomplete.ts(1 hunks)
🔇 Additional comments (6)
apps/mail/components/create/editor-autocomplete.ts (6)
52-57: Don't directly modify input parameters.The function is directly modifying properties of the input parameter
opts, which can cause unexpected side effects if the same options object is reused elsewhere. Instead, create local copies of the arrays.Apply this fix:
- opts.suggestions.timeBased = [ - `Good ${timeOfDay} ${name},`, - `I hope you're having a good ${timeOfDay} ${name},`, - `Wishing you a wonderful ${timeOfDay} ${name},`, - ]; + const timeBased = [ + `Good ${timeOfDay} ${name},`, + `I hope you're having a good ${timeOfDay} ${name},`, + `Wishing you a wonderful ${timeOfDay} ${name},`, + ];
62-67: Don't directly modify input parameters.Similar to the previous issue, this code is directly modifying properties of the input parameter.
Apply this fix:
- opts.suggestions.contextBased = [ - `Thank you for your email regarding ${lastEmail}.`, - `I received your message about ${lastEmail}.`, - `I understand your point about ${lastEmail}.`, - ]; + const contextBased = [ + `Thank you for your email regarding ${lastEmail}.`, + `I received your message about ${lastEmail}.`, + `I understand your point about ${lastEmail}.`, + ];
70-81: Don't directly modify input parameters.The function is directly modifying the commonPhrases property of the input parameter.
Apply this fix:
- opts.suggestions.commonPhrases = [ + const commonPhrases = [ `I hope this email finds you well.`, `I wanted to follow up on our previous conversation.`, `I'm writing to discuss...`, `I would like to schedule a meeting to discuss...`, `Please let me know if you have any questions.`, `I look forward to your response.`, `I appreciate your time and consideration.`, `I'm happy to help with anything else you need.`, `Please don't hesitate to reach out if you need any clarification.`, `I'll be in touch soon.`, ];
83-99: Don't directly modify input parameters.The function is still directly modifying the input parameter by pushing to
opts.suggestions.openers. This doesn't address the previous review comments that suggested creating copies of arrays.Apply this fix:
- if (opts.sender) { - const { name } = opts.sender; - if (name) { - opts.suggestions.openers?.push( - `Hello ${name},`, - `Hi ${name},`, - `Dear ${name},`, - `Good morning ${name},`, - `Good afternoon ${name},`, - `Good evening ${name},`, - `I hope you're doing well ${name},`, - `I trust this email finds you well ${name},`, - `I hope you're having a great day ${name},`, - ); - } - } + // Create a copy of openers array + let openers = [...(opts.suggestions.openers || [])]; + if (opts.sender?.name) { + const { name } = opts.sender; + openers = [ + ...openers, + `Hello ${name},`, + `Hi ${name},`, + `Dear ${name},`, + `Good morning ${name},`, + `Good afternoon ${name},`, + `Good evening ${name},`, + `I hope you're doing well ${name},`, + `I trust this email finds you well ${name},`, + `I hope you're having a great day ${name},`, + ]; + }
101-118: Don't directly modify input parameters.The function is still directly modifying the input parameter by pushing to
opts.suggestions.closers. This doesn't address the previous review comments that suggested creating copies of arrays.Apply this fix:
- if (opts.myInfo) { - const { name } = opts.myInfo; - if (name) { - opts.suggestions.closers?.push( - `Best regards,\n${name}`, - `Kind regards,\n${name}`, - `Sincerely,\n${name}`, - `Thanks,\n${name}`, - `Best wishes,\n${name}`, - `Warm regards,\n${name}`, - `Cheers,\n${name}`, - `Take care,\n${name}`, - `Looking forward to your response,\n${name}`, - `I look forward to hearing from you,\n${name}`, - ); - } - } + // Create a copy of closers array + let closers = [...(opts.suggestions.closers || [])]; + if (opts.myInfo?.name) { + const { name } = opts.myInfo; + closers = [ + ...closers, + `Best regards,\n${name}`, + `Kind regards,\n${name}`, + `Sincerely,\n${name}`, + `Thanks,\n${name}`, + `Best wishes,\n${name}`, + `Warm regards,\n${name}`, + `Cheers,\n${name}`, + `Take care,\n${name}`, + `Looking forward to your response,\n${name}`, + `I look forward to hearing from you,\n${name}`, + ]; + }
120-127: Update the allSuggestions array to use the copied arrays.After implementing the previous fixes to create copies of arrays, you need to update this section to use those copied arrays.
Apply this fix:
const allSuggestions = [ - ...(opts.suggestions.openers || []), - ...(opts.suggestions.closers || []), - ...(opts.suggestions.custom || []), - ...(opts.suggestions.commonPhrases || []), - ...(opts.suggestions.timeBased || []), - ...(opts.suggestions.contextBased || []), + ...openers, + ...closers, + ...(opts.suggestions.custom || []), + ...commonPhrases, + ...(timeBased || []), + ...(contextBased || []), ];
- added delay of 2 matching characters
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/mail/components/create/editor-autocomplete.ts (1)
213-214: Consider addressing the TypeScript type incompatibility.The @ts-expect-error comment suggests there's a type compatibility issue between Tiptap and ProseMirror. While the current approach works, it would be better to find a more type-safe solution if possible.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/mail/components/create/editor-autocomplete.ts(1 hunks)apps/mail/components/create/email-phrases.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/mail/components/create/email-phrases.ts
🧰 Additional context used
🧬 Code Definitions (1)
apps/mail/components/create/editor-autocomplete.ts (1)
apps/mail/components/create/email-phrases.ts (1)
emailPhrases(7-74)
🪛 Biome (1.9.4)
apps/mail/components/create/editor-autocomplete.ts
[error] 211-211: This code is unreachable
... because either this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... or this statement will return from the function beforehand
(lint/correctness/noUnreachable)
🔇 Additional comments (10)
apps/mail/components/create/editor-autocomplete.ts (10)
8-11: Nice use of clear interface definitions.The
SenderInfointerface is well structured and provides good optional fields for name and email, making it flexible for different use cases.
13-20: Good organization of email suggestion categories.The
EmailSuggestionsinterface provides a comprehensive categorization of different types of email phrases, which enables rich contextual suggestions.
22-32: Well-designed options interface with contextual information.The
AutoCompleteOptionsinterface elegantly combines suggestions, sender information, and contextual data to create personalized autocomplete suggestions.
85-98: Avoid modifying input parameters and consider graceful handling of missing arrays.The code correctly customizes openers with the sender's name but creates a new array for the
opts.suggestions.openersproperty instead of modifying it.
100-116: Avoid directly modifying input parameters.The function modifies the input parameter
opts.suggestions.closersby pushing new items to it, which could cause unexpected side effects if the same options object is reused. Consider creating a copy of the array instead.- if (opts.myInfo) { - const { name } = opts.myInfo; - if (name) { - opts.suggestions.closers?.push( - `Best regards,\n${name}`, - `Kind regards,\n${name}`, - `Sincerely,\n${name}`, - `Thanks,\n${name}`, - `Best wishes,\n${name}`, - `Warm regards,\n${name}`, - `Cheers,\n${name}`, - `Take care,\n${name}`, - `Looking forward to your response,\n${name}`, - `I look forward to hearing from you,\n${name}`, - ); - } - } + let closers = [...(opts.suggestions.closers || [])]; + if (opts.myInfo?.name) { + closers = [ + ...closers, + `Best regards,\n${opts.myInfo.name}`, + `Kind regards,\n${opts.myInfo.name}`, + `Sincerely,\n${opts.myInfo.name}`, + `Thanks,\n${opts.myInfo.name}`, + `Best wishes,\n${opts.myInfo.name}`, + `Warm regards,\n${opts.myInfo.name}`, + `Cheers,\n${opts.myInfo.name}`, + `Take care,\n${opts.myInfo.name}`, + `Looking forward to your response,\n${opts.myInfo.name}`, + `I look forward to hearing from you,\n${opts.myInfo.name}`, + ]; + }
118-126: Update the allSuggestions array to use the copied arrays.After implementing the previous suggestion, you'll need to update this section to use the copied closers array.
127-160: The suggestion filtering algorithm is well-implemented.The logic checks for:
- Text matching with the current input
- Not already used suggestions
- Context-appropriate suggestions (avoiding greetings in the middle of an email)
- Avoiding similar greetings
The sorting prioritizes exact matches and shorter suggestions first, which is a good UX choice.
169-184: Good validation of selection state before attempting to process suggestions.The code properly checks for valid cursor positions and text selection before proceeding, which prevents potential runtime errors.
196-209: Well-implemented error handling for suggestion application.The try-catch block ensures that any errors during suggestion application are caught and logged, preventing the entire editor from crashing.
241-250: Nice implementation of the ghost text suggestion UI.The decoration widget implementation creates a subtle and effective visual cue for the available suggestion, and the CSS class allows for styling the shimmering effect defined in the imported CSS file.
| return false; | ||
| } | ||
|
|
||
| return true; |
There was a problem hiding this comment.
Remove unreachable code.
This return statement is unreachable as all code paths above it either return true or false.
- return true;📝 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.
| return true; | |
| // ... (previous code remains unchanged) | |
| // Unreachable return statement removed | |
| // ... (subsequent code remains unchanged) |
🧰 Tools
🪛 Biome (1.9.4)
[error] 211-211: This code is unreachable
... because either this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... this statement, ...
... or this statement will return from the function beforehand
(lint/correctness/noUnreachable)
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/mail/components/create/editor.tsx (1)
506-537: Well-organized autocomplete configuration.The
AutoCompleteextension is properly configured with categorized suggestions for email openers, closers, and custom phrases. The implementation correctly passes user and sender context to enable personalized suggestions.The predefined suggestions provide a good starting point, but consider expanding these over time based on user feedback.
Consider extracting these predefined phrase arrays into a separate configuration file to make them easier to maintain and update in the future. This would also allow for localization if needed.
-extensions={[ - ...defaultExtensions, - Markdown, - AutoComplete.configure({ - suggestions: { - openers: [ - 'Hi there,', - 'Hello,', - 'Dear', - 'Greetings,', - 'Good morning,', - 'Good afternoon,', - 'Good evening,', - ], - closers: [ - 'Best regards,', - 'Kind regards,', - 'Sincerely,', - 'Thanks,', - 'Thank you,', - 'Cheers,', - ], - custom: [ - 'I hope this email finds you well.', - 'I look forward to hearing from you.', - 'Please let me know if you have any questions.', - ], - }, - sender: senderInfo, - myInfo: myInfo, - }), -]} +import { emailSuggestions } from './email-suggestions'; + +extensions={[ + ...defaultExtensions, + Markdown, + AutoComplete.configure({ + suggestions: emailSuggestions, + sender: senderInfo, + myInfo: myInfo, + }), +]}apps/mail/components/mail/reply-composer.tsx (1)
507-514: Properly passing user and sender context to Editor.The implementation correctly passes the user's information and the email sender's information to the Editor component, enabling personalized autocomplete suggestions.
The optional chaining (
?.) is used appropriately to handle potential undefined values.Consider adding a comment to explain the purpose of passing these props to enhance code readability:
+// Pass user and sender information to enable personalized autocomplete suggestions myInfo={{ name: session?.user.name, email: session?.user.email, }} senderInfo={{ name: emailData[0]?.sender?.name, email: emailData[0]?.sender?.email, }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/mail/components/create/editor.tsx(5 hunks)apps/mail/components/mail/reply-composer.tsx(3 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
apps/mail/components/create/editor.tsx (1)
apps/mail/components/create/editor-autocomplete.ts (1)
AutoComplete(34-255)
apps/mail/components/mail/reply-composer.tsx (1)
packages/db/src/schema.ts (1)
session(18-29)
🔇 Additional comments (5)
apps/mail/components/create/editor.tsx (3)
47-47: New autocomplete feature added.This import adds the autocomplete functionality required for the enhanced editor experience.
75-82: Clear and well-structured interface extension.The
EditorPropsinterface has been properly extended to include optional user and sender information fields. This provides the necessary context for the autocomplete functionality to generate personalized suggestions.
390-391: Props correctly added to function parameters.The new props are correctly added to the
Editorfunction parameters, matching the interface changes defined above.apps/mail/components/mail/reply-composer.tsx (2)
3-10: Good import organization.The import section is well-structured. The addition of
useSessionfrom the auth-client library is appropriate for accessing user session data.Also applies to: 18-18
107-107: Added session data retrieval.Correctly implementing user session data retrieval using the
useSessionhook from the auth-client library.
Summary by CodeRabbit
New Features
Style