Skip to content

Comments

all round fixes#1697

Merged
MrgSub merged 1 commit intostagingfrom
07-09-all_round_fixes
Jul 9, 2025
Merged

all round fixes#1697
MrgSub merged 1 commit intostagingfrom
07-09-all_round_fixes

Conversation

@MrgSub
Copy link
Collaborator

@MrgSub MrgSub commented Jul 9, 2025

Add "Open in New Tab" Feature and Fix Email Handling

Description

This PR adds an "Open in New Tab" feature to the thread context menu, allowing users to open email threads in a new browser tab. It also fixes several issues with email handling:

  1. Adds an "Open in New Tab" option to the thread context menu with an ExternalLink icon
  2. Improves draft handling by saving drafts before sending emails
  3. Fixes the identification of the latest email in threads by properly filtering out drafts
  4. Updates the styling of the "Mark as Important" icon in the thread context menu
  5. Removes unnecessary console.log statements and async/await usage where not needed
  6. Fixes the web search tool to work with or without data streaming

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 🎨 UI/UX improvement
  • ⚡ Performance improvement

Areas Affected

  • Email Integration (Gmail, IMAP, etc.)
  • User Interface/Experience
  • Data Storage/Management

Testing Done

  • Manual testing performed

Checklist

  • I have performed a self-review of my code
  • My changes generate no new warnings

Additional Notes

The "Open in New Tab" feature improves workflow for users who need to reference multiple email threads simultaneously. The draft handling improvements ensure that emails are properly saved before sending, preventing data loss.

Summary by CodeRabbit

  • New Features

    • Added "Open in New Tab" action to the email thread context menu for easier access to threads in a new browser tab.
  • Improvements

    • Drafts are now saved automatically before sending emails, ensuring no unsaved changes are lost.
    • More accurate detection of the latest email in a thread.
    • Localization updated with a new string for the "Open in New Tab" action.
    • Email and thread operations now consistently use an updated agent interface for improved backend handling.
  • Bug Fixes

    • Latest message and reply counts now exclude drafts, improving thread accuracy.
  • Style

    • Minor visual updates to icons and CSS class ordering.
    • Code formatting and import reordering for consistency.
  • Chores

    • Internal state updates are now handled synchronously for improved performance.
    • Removed unnecessary logging and simplified cache management.
    • Removed deprecated notification function and related imports.
    • Simplified query cache clearing on account switch.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 9, 2025

Walkthrough

This update introduces a new "Open in New Tab" action to the email thread context menu and updates icon styling. It also refines draft and reply handling, improves thread data retrieval, adjusts event handling, and removes or simplifies asynchronous state updates and cache management. Minor localization and formatting changes are included.

Changes

File(s) Change Summary
apps/mail/components/context/thread-context.tsx Added "Open in New Tab" context menu action with handler and updated icon styling for "toggle-important".
apps/mail/components/create/create-email.tsx Minor import reordering, formatting, and removal of unnecessary awaits in state setters.
apps/mail/components/create/email-composer.tsx Ensured drafts are saved before sending, removed debug logs.
apps/mail/components/mail/mail-display.tsx Switched to useThread for thread data, updated last-email logic, removed popover wrapper, improved event handling.
apps/mail/components/mail/reply-composer.tsx Added draftId to send payload, removed awaits from state setters.
apps/mail/components/ui/ai-sidebar.tsx Made setViewMode synchronous, removed unnecessary async/await.
apps/mail/components/ui/app-sidebar.tsx Reordered CSS classes, removed awaits from state setters, made state resets sequential.
apps/mail/components/ui/nav-user.tsx Simplified cache invalidation by replacing multiple removals with a single cache clear call.
apps/mail/messages/en.json Added "openInNewTab" localization string.
apps/mail/providers/query-provider.tsx Removed selective query dehydration and cache truncation logic.
apps/server/src/lib/driver/google.ts Explicitly excluded drafts in latest message logic, minor formatting changes.
apps/server/src/routes/agent/tools.ts Made dataStream optional in webSearch, added conditional streaming/non-streaming response logic.
apps/server/src/routes/chat.ts Excluded drafts from latest message and reply count calculations; switched from driver to agent abstraction.
apps/server/src/trpc/routes/ai/compose.ts Reordered imports, invoked webSearch() directly in tools object.
apps/server/src/lib/driver/microsoft.ts Added stub method listHistory returning empty history.
apps/server/src/lib/driver/types.ts Updated internalDate type to allow null in ParsedDraft interface.
apps/server/src/lib/server-utils.ts Removed notifyUser function.
apps/server/src/pipelines.effect.ts Removed unused imports connectionToDriver and notifyUser.
apps/server/src/routes/ai.ts Replaced all driver usage with agent obtained via getZeroAgent; adjusted method calls accordingly.
apps/server/src/services/mcp-service/mcp.ts Removed entire ZeroMCP class and related driver retrieval function.
apps/server/src/trpc/trpc.ts Removed unused import connectionToDriver.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ThreadContextMenu
    participant Browser

    User->>ThreadContextMenu: Right-clicks thread, selects "Open in New Tab"
    ThreadContextMenu->>Browser: Opens new tab with constructed thread URL
Loading
sequenceDiagram
    participant User
    participant EmailComposer
    participant DraftManager
    participant Server

    User->>EmailComposer: Clicks "Send"
    EmailComposer->>DraftManager: Checks for unsaved changes
    alt Unsaved changes
        DraftManager->>Server: saveDraft()
    end
    EmailComposer->>Server: sendEmail (with draftId if available)
Loading

Possibly related PRs

  • context #595: Introduces the entire ThreadContextMenu component that this PR modifies, making it foundational to these changes.
  • refactoring & improvements #502: Modifies the ThreadContextMenu component by simplifying mutate calls and removing dependencies, related to this PR’s updates in the same component.
  • context menu #708: Adds new handlers and refactors reply-related actions in ThreadContextMenu, related to this PR’s addition of menu actions and icon updates.

Poem

A bunny with code in its hat,
Adds a menu—imagine that!
"Open in New Tab," now just a click,
Drafts are saved—no more tricks.
Icons refreshed, events refined,
In this patch, new features combined.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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

@MrgSub MrgSub marked this pull request as ready for review July 9, 2025 22:23
Copy link
Collaborator Author

MrgSub commented Jul 9, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

Comprehensive improvements to email handling and UI functionality across multiple components, focusing on draft management, thread organization, and performance optimizations.

  • Enhanced draft handling in apps/mail/components/create/email-composer.tsx to properly save draft state before sending emails
  • Improved thread message filtering in apps/server/src/routes/chat.ts to correctly handle draft messages in thread counts and latest message calculations
  • Added 'Open in New Tab' functionality in apps/mail/components/context/thread-context.tsx with corresponding i18n support
  • Optimized performance by removing unnecessary async/await operations in multiple components including app-sidebar.tsx, nav-user.tsx, and ai-sidebar.tsx
  • Enhanced web search flexibility in apps/server/src/routes/agent/tools.ts by adding support for both streaming and non-streaming responses

13 files reviewed, 2 comments
Edit PR Review Bot Settings | Greptile

cursor[bot]

This comment was marked as outdated.

Copy link
Contributor

@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

🔭 Outside diff range comments (1)
apps/server/src/routes/agent/tools.ts (1)

341-368: Refactor to eliminate code duplication and improve type safety.

The conditional logic correctly handles streaming vs non-streaming responses, but there are areas for improvement:

  1. Code duplication: The system messages are duplicated in both branches
  2. Type safety: The function returns different types ({type: 'streaming_response', query} vs string), which could cause runtime issues

Consider this refactoring to eliminate duplication and improve type safety:

    execute: async ({ query }) => {
      try {
+        const systemMessages = [
+          { role: 'system', content: 'Be precise and concise.' },
+          { role: 'system', content: 'Do not include sources in your response.' },
+          { role: 'system', content: 'Do not use markdown formatting in your response.' },
+          { role: 'user', content: query },
+        ] as const;
+
        if (dataStream) {
          const response = streamText({
            model: perplexity('sonar'),
-            messages: [
-              { role: 'system', content: 'Be precise and concise.' },
-              { role: 'system', content: 'Do not include sources in your response.' },
-              { role: 'system', content: 'Do not use markdown formatting in your response.' },
-              { role: 'user', content: query },
-            ],
+            messages: systemMessages,
            maxTokens: 1024,
          });
          response.mergeIntoDataStream(dataStream);

-          return { type: 'streaming_response', query };
+          return `Streaming web search results for: ${query}`;
        }

        const response = await generateText({
          model: perplexity('sonar'),
-          messages: [
-            { role: 'system', content: 'Be precise and concise.' },
-            { role: 'system', content: 'Do not include sources in your response.' },
-            { role: 'system', content: 'Do not use markdown formatting in your response.' },
-            { role: 'user', content: query },
-          ],
+          messages: systemMessages,
          maxTokens: 1024,
        });

        return response.text;
🧹 Nitpick comments (2)
apps/server/src/routes/agent/tools.ts (1)

333-374: Consider potential naming conflict with other webSearch implementations.

There's another webSearch function in apps/server/src/trpc/routes/ai/webSearch.ts that serves a different purpose (TRPC mutation vs tool). This could lead to confusion and potential import conflicts.

Consider renaming this function to webSearchTool to clearly distinguish it from the TRPC procedure:

-export const webSearch = (dataStream?: DataStreamWriter) =>
+export const webSearchTool = (dataStream?: DataStreamWriter) =>

And update the usage in the tools object:

-    [Tools.WebSearch]: webSearch(dataStream),
+    [Tools.WebSearch]: webSearchTool(dataStream),
apps/mail/components/create/email-composer.tsx (1)

468-469: Consider error handling for draft saving before send.

The conditional draft save before sending is a good improvement that ensures consistency. However, consider what should happen if saveDraft() fails - should the send operation continue or be aborted?

You might want to add explicit error handling:

 // Save draft before sending, we want to send drafts instead of sending new emails
-if (hasUnsavedChanges) await saveDraft();
+if (hasUnsavedChanges) {
+  try {
+    await saveDraft();
+  } catch (error) {
+    console.error('Failed to save draft before sending:', error);
+    toast.error('Failed to save draft before sending');
+    return; // or continue based on your preference
+  }
+}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c7b637f and 0996839.

📒 Files selected for processing (14)
  • apps/mail/components/context/thread-context.tsx (3 hunks)
  • apps/mail/components/create/create-email.tsx (4 hunks)
  • apps/mail/components/create/email-composer.tsx (1 hunks)
  • apps/mail/components/mail/mail-display.tsx (6 hunks)
  • apps/mail/components/mail/reply-composer.tsx (2 hunks)
  • apps/mail/components/ui/ai-sidebar.tsx (1 hunks)
  • apps/mail/components/ui/app-sidebar.tsx (2 hunks)
  • apps/mail/components/ui/nav-user.tsx (1 hunks)
  • apps/mail/messages/en.json (1 hunks)
  • apps/mail/providers/query-provider.tsx (0 hunks)
  • apps/server/src/lib/driver/google.ts (4 hunks)
  • apps/server/src/routes/agent/tools.ts (2 hunks)
  • apps/server/src/routes/chat.ts (1 hunks)
  • apps/server/src/trpc/routes/ai/compose.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • apps/mail/providers/query-provider.tsx
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: danteissaias
PR: Mail-0/Zero#618
File: apps/mail/components/mail/mail-iframe.tsx:12-12
Timestamp: 2025-04-07T20:46:11.697Z
Learning: In the Mail-0/Zero application, sender emails are guaranteed to be non-empty when passed to components that handle them, making additional empty string validation unnecessary.
apps/mail/components/ui/nav-user.tsx (2)
Learnt from: dakdevs
PR: Mail-0/Zero#764
File: apps/mail/lib/ai.ts:50-51
Timestamp: 2025-04-25T08:33:16.956Z
Learning: In Next.js 15, the `headers()` function from 'next/headers' is asynchronous and requires using `await` when called, which is a breaking change from previous versions where it was synchronous.
Learnt from: dakdevs
PR: Mail-0/Zero#764
File: apps/mail/lib/ai.ts:50-51
Timestamp: 2025-04-25T08:33:16.956Z
Learning: In Next.js 15, the `headers()` function from 'next/headers' is asynchronous and requires using `await` when called, which is a breaking change from previous versions where it was synchronous.
apps/mail/components/ui/ai-sidebar.tsx (2)
Learnt from: dakdevs
PR: Mail-0/Zero#764
File: apps/mail/lib/ai.ts:50-51
Timestamp: 2025-04-25T08:33:16.956Z
Learning: In Next.js 15, the `headers()` function from 'next/headers' is asynchronous and requires using `await` when called, which is a breaking change from previous versions where it was synchronous.
Learnt from: dakdevs
PR: Mail-0/Zero#764
File: apps/mail/lib/ai.ts:50-51
Timestamp: 2025-04-25T08:33:16.956Z
Learning: In Next.js 15, the `headers()` function from 'next/headers' is asynchronous and requires using `await` when called, which is a breaking change from previous versions where it was synchronous.
apps/mail/components/create/create-email.tsx (3)
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:331-331
Timestamp: 2025-06-28T03:56:09.376Z
Learning: In apps/server/src/trpc/routes/mail.ts, the user indicated they are not using ISO format for the scheduleAt parameter, despite the frontend code showing toISOString() usage in the ScheduleSendPicker component.
Learnt from: retrogtx
PR: Mail-0/Zero#1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
apps/mail/components/create/email-composer.tsx (2)
Learnt from: retrogtx
PR: Mail-0/Zero#1622
File: apps/server/src/lib/email-verification.ts:189-189
Timestamp: 2025-07-05T05:27:24.592Z
Learning: During testing phases, debug logging should be kept active in apps/server/src/lib/email-verification.ts for BIMI validation and email verification debugging, even if it's verbose.
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
apps/mail/components/ui/app-sidebar.tsx (2)
Learnt from: danteissaias
PR: Mail-0/Zero#902
File: apps/mail/components/connection/add.tsx:77-77
Timestamp: 2025-05-07T16:55:46.513Z
Learning: For the "Upgrade" link in AddConnectionDialog, using a proper <button> element instead of a <span> with onClick is recognized as an accessibility improvement but was deferred as out of scope in PR #902 (CSS variables PR).
Learnt from: retrogtx
PR: Mail-0/Zero#1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
apps/mail/components/context/thread-context.tsx (1)
Learnt from: retrogtx
PR: Mail-0/Zero#1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
apps/server/src/lib/driver/google.ts (2)
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:331-331
Timestamp: 2025-06-28T03:56:09.376Z
Learning: In apps/server/src/trpc/routes/mail.ts, the user indicated they are not using ISO format for the scheduleAt parameter, despite the frontend code showing toISOString() usage in the ScheduleSendPicker component.
apps/mail/components/mail/mail-display.tsx (1)
Learnt from: retrogtx
PR: Mail-0/Zero#1328
File: apps/mail/lib/hotkeys/mail-list-hotkeys.tsx:202-209
Timestamp: 2025-06-18T17:26:50.918Z
Learning: In apps/mail/lib/hotkeys/mail-list-hotkeys.tsx, the switchCategoryByIndex function using hardcoded indices for category hotkeys does not break when users reorder categories, contrary to the theoretical index-shifting issue. The actual implementation has constraints or mechanisms that prevent hotkey targeting issues.
🧬 Code Graph Analysis (3)
apps/mail/components/create/create-email.tsx (2)
apps/server/src/types.ts (1)
  • Attachment (152-159)
apps/mail/types/index.ts (1)
  • Attachment (98-105)
apps/server/src/routes/agent/tools.ts (1)
apps/server/src/trpc/routes/ai/webSearch.ts (1)
  • webSearch (6-17)
apps/mail/components/context/thread-context.tsx (1)
apps/mail/components/icons/icons.tsx (1)
  • ExclamationCircle (415-432)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor BugBot
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (24)
apps/server/src/trpc/routes/ai/compose.ts (3)

6-11: Import reordering improves code organization.

The reordering of imports creates better logical grouping by placing related imports together. This enhances code readability and follows good practices.


40-41: Trailing commas enhance code consistency.

Adding trailing commas is a good practice that makes future diffs cleaner and follows modern JavaScript/TypeScript conventions.


107-107: Function invocation aligns with updated signature.

The change from webSearch to webSearch() correctly adapts to the updated function signature that now accepts an optional dataStream parameter. Since no dataStream is provided here, the function will use the non-streaming branch.

apps/server/src/routes/agent/tools.ts (1)

333-333: Function signature change enables flexible usage.

Making the dataStream parameter optional allows the function to work in both streaming and non-streaming contexts, which improves its reusability across different parts of the application.

apps/mail/components/ui/nav-user.tsx (1)

96-96: LGTM - Fire-and-forget mutation pattern applied consistently.

The removal of await from the mutation call aligns with the broader pattern in this PR to simplify state updates. The mutation now fires without blocking the rest of the function execution, which can improve perceived performance.

apps/mail/components/ui/ai-sidebar.tsx (1)

282-284: LGTM - Consistent async simplification.

The removal of async and await keywords simplifies the function while maintaining the same functionality. The query state update is now fire-and-forget, which is appropriate for this use case.

apps/mail/components/ui/app-sidebar.tsx (2)

160-160: Minor CSS class reordering - no functional impact.

The CSS classes have been reordered without affecting the visual appearance or functionality.


187-197: LGTM - Removed unnecessary await from state setters.

React state setters are synchronous operations that don't need to be awaited. This change simplifies the code while maintaining the same functionality.

apps/mail/components/mail/reply-composer.tsx (2)

194-194: LGTM - Enhanced draft handling.

Adding draftId to the email payload allows proper association between sent emails and their corresponding drafts, improving the draft workflow.


257-259: LGTM - Consistent removal of unnecessary await.

React state setters don't need to be awaited. This change aligns with the consistent pattern applied throughout the codebase.

apps/mail/messages/en.json (1)

373-374: LGTM - New localization key for "Open in new tab" feature.

The new localization key follows the established naming convention and supports the new context menu action.

apps/mail/components/create/create-email.tsx (2)

117-117: LGTM: Correctly removed unnecessary await from state setter.

State setters from useQueryState are synchronous functions that don't return promises, so removing the await is correct and improves code clarity.


15-15: LGTM: Good formatting and style improvements.

The changes include:

  • Moving the Attachment import to an earlier position for better organization
  • Adding consistent semicolons
  • Reformatting the attachment conversion logic for better readability

These changes improve code consistency and readability without affecting functionality.

Also applies to: 31-31, 165-165, 168-168, 172-173

apps/server/src/routes/chat.ts (2)

1212-1212: LGTM! Improved draft exclusion logic.

The explicit check e.isDraft !== true is more robust than !e.isDraft as it specifically excludes only messages that are explicitly marked as drafts, properly handling cases where isDraft might be undefined or null.


1214-1214: LGTM! Consistent draft exclusion for reply count.

The change aligns with the latest message selection logic and ensures that draft messages are consistently excluded from the total replies count.

apps/mail/components/context/thread-context.tsx (4)

15-15: LGTM! Proper import for new feature.

The ExternalLink import is correctly added to support the new "Open in New Tab" functionality.


268-270: LGTM! Clean implementation of new tab functionality.

The handleOpenInNewTab function correctly constructs the URL using the current folder and thread ID, and properly opens it in a new tab.


274-280: LGTM! Well-structured context menu action.

The new "Open in New Tab" action follows the existing pattern with proper localization, icon, and handler function. The placement as the first action in the primary actions array makes sense from a UX perspective.


443-443: LGTM! Consistent icon styling approach.

The change from opacity-based styling to explicit fill colors for light and dark modes aligns with the pattern used for other icons in the same actions array (like the Mail icon on line 431).

apps/server/src/lib/driver/google.ts (2)

392-392: LGTM! Consistent draft exclusion logic.

The explicit check e.isDraft !== true aligns with the similar change in apps/server/src/routes/chat.ts and ensures consistent draft handling across the server-side components.


127-127: LGTM! Minor formatting improvements.

The formatting changes in the parseDraft method improve code readability without affecting functionality. The changes include better line breaks for async operations and filtering logic.

Also applies to: 132-132, 153-153, 159-162, 178-179

apps/mail/components/mail/mail-display.tsx (3)

49-49: LGTM!

The import of useThread hook is correctly added to support the new thread data functionality.


82-85: Good defensive programming with event handling.

The addition of preventDefault() and stopPropagation() calls with explicit MouseEvent parameter improves event handling by preventing unwanted default behaviors and event bubbling during text selection.


783-783: ThreadData.latest.id is reliably the most recent non-draft message—no further action needed.

  • In GoogleDriver.get, messages is populated from Gmail’s API (which returns messages in chronological order), and latest is set via messages.findLast(e ⇒ !e.isDraft).
  • The useThread hook preserves this latest value unaltered in threadData.latest.

Change to ID-based last-email detection is safe and can be approved.

Comment on lines +1361 to 1364
<>
{searchQuery && (
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Text selection functionality is broken.

The TextSelectionPopover wrapper has been completely removed and replaced with a React fragment, which breaks the text selection functionality entirely. The TextSelectionPopover component provides essential event listeners and selection logic that enables users to select text and get search/copy options.

The TextSelectionPopover component should wrap the main content to maintain text selection functionality:

-      <>
+      <TextSelectionPopover onSearch={setSearchQuery}>
         {searchQuery && (
           <MoreAboutQuery
             query={searchQuery}
             open={!!searchQuery}
             onOpenChange={(open) => (open ? void 0 : setSearchQuery(null))}
           />
         )}
         {/* ... rest of content ... */}
-      </>
+      </TextSelectionPopover>
📝 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
<>
{searchQuery && (
<TextSelectionPopover onSearch={setSearchQuery}>
{searchQuery && (
<MoreAboutQuery
query={searchQuery}
open={!!searchQuery}
onOpenChange={(open) => (open ? void 0 : setSearchQuery(null))}
/>
)}
{/* ... rest of content ... */}
</TextSelectionPopover>
🤖 Prompt for AI Agents
In apps/mail/components/mail/mail-display.tsx around lines 1361 to 1362, the
TextSelectionPopover component was removed and replaced with a React fragment,
breaking text selection functionality. To fix this, wrap the main content inside
the TextSelectionPopover component again to restore the event listeners and
selection logic needed for text selection and related options.

@MrgSub MrgSub force-pushed the 07-09-all_round_fixes branch from 1c9c239 to 9ba3ac8 Compare July 9, 2025 22:54
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Removed UI Breaks Text Selection

The TextSelectionPopover component was removed, but its associated handleSelectionChange function continues to prevent default browser text selection behavior by calling e.preventDefault() and e.stopPropagation() when text is selected. This results in text selection being intercepted without any visible UI or intended functionality, breaking normal text selection and potentially copy/paste.

apps/mail/components/mail/mail-display.tsx#L81-L87

const handleSelectionChange = useCallback((e: MouseEvent) => {
if (window.getSelection()?.toString().trim()) {
e.preventDefault();
e.stopPropagation();
}

apps/mail/components/mail/mail-display.tsx#L1362-L1864

>
<>
{searchQuery && (
<MoreAboutQuery
query={searchQuery}
open={!!searchQuery}
onOpenChange={(open) => (open ? void 0 : setSearchQuery(null))}
/>
)}
{researchSender && (
<MoreAboutPerson
open={!!researchSender}
onOpenChange={(open) => (open ? void 0 : setResearchSender(null))}
person={researchSender}
/>
)}
<div className="relative h-full overflow-y-auto">
<div className={cn('px-4', index === 0 && 'border-b py-4')}>
{index === 0 && (
<>
<span className="inline-flex items-center gap-2 font-medium text-black dark:text-white">
<span>
{emailData.subject}{' '}
<span className="text-muted-foreground dark:text-[#8C8C8C]">
{totalEmails && totalEmails > 1 && `[${totalEmails}]`}
</span>
</span>
</span>
<div className="mt-2 flex items-center gap-2">
{emailData?.tags?.length ? (
<MailDisplayLabels labels={emailData?.tags.map((t) => t.name) || []} />
) : null}
{emailData?.tags?.length ? (
<div className="bg-iconLight dark:bg-iconDark/20 relative h-3 w-0.5 rounded-full" />
) : null}
<RenderLabels labels={threadLabels} />
{threadLabels.length ? (
<div className="bg-iconLight dark:bg-iconDark/20 relative h-3 w-0.5 rounded-full" />
) : null}
<div className="text-muted-foreground flex items-center gap-2 text-sm dark:text-[#8C8C8C]">
{(() => {
if (people.length <= 2) {
return people.map(renderPerson);
}
// Only show first two people plus count if we have at least two people
const firstPerson = people[0];
const secondPerson = people[1];
if (firstPerson && secondPerson) {
return (
<>
{renderPerson(firstPerson)}
{renderPerson(secondPerson)}
<Tooltip>
<TooltipTrigger asChild>
<span className="text-sm">
+{people.length - 2}{' '}
{people.length - 2 === 1 ? 'other' : 'others'}
</span>
</TooltipTrigger>
<TooltipContent className="flex flex-col gap-1">
{people.slice(2).map((person, index) => (
<div key={index}>{renderPerson(person)}</div>
))}
</TooltipContent>
</Tooltip>
</>
);
}
return null;
})()}
</div>
</div>
{brainState?.enabled && <AiSummary />}
{threadAttachments && threadAttachments.length > 0 && (
<ThreadAttachments attachments={threadAttachments} />
)}
</>
)}
</div>
<div
className="flex cursor-pointer flex-col pb-2 transition-all duration-200"
onClick={toggleCollapse}
>
<div className="mt-3 flex w-full items-start justify-between gap-4 px-4">
<div className="flex w-full justify-center gap-4">
<Avatar className="mt-3 h-8 w-8 rounded-full border dark:border-none">
<AvatarImage
className="rounded-full"
src={getEmailLogo(emailData?.sender?.email)}
/>
<AvatarFallback className="rounded-full bg-[#FFFFFF] font-bold text-[#9F9F9F] dark:bg-[#373737]">
{getFirstLetterCharacter(emailData?.sender?.name)}
</AvatarFallback>
</Avatar>
<div className="flex w-full items-center justify-between">
<div className="flex w-full items-center justify-start">
<div className="flex w-full flex-col">
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-1">
<span
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setResearchSender({
name: emailData?.sender?.name || '',
email: emailData?.sender?.email || '',
// extra: emailData?.sender?.extra || '',
});
}}
className="hover:bg-muted max-w-36 truncate whitespace-nowrap font-semibold md:max-w-none"
>
{cleanNameDisplay(emailData?.sender?.name)}
</span>
<Popover open={openDetailsPopover} onOpenChange={handlePopoverChange}>
<PopoverTrigger asChild>
<button
className="hover:bg-iconLight/10 dark:hover:bg-iconDark/20 flex items-center gap-2 rounded-md p-2"
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setOpenDetailsPopover(!openDetailsPopover);
}}
ref={triggerRef}
>
<p className="text-muted-foreground text-xs underline dark:text-[#8C8C8C]">
{m['common.mailDisplay.details']()}
</p>
</button>
</PopoverTrigger>
<PopoverContent
className="dark:bg-panelDark flex w-[420px] overflow-auto rounded-lg border p-4 text-left shadow-lg"
onBlur={(e) => {
if (!triggerRef.current?.contains(e.relatedTarget)) {
setOpenDetailsPopover(false);
}
}}
onClick={(e) => e.stopPropagation()}
>
<div className="space-y-1 text-sm">
<div className="flex">
<span className="w-24 text-end text-gray-500">
{m['common.mailDisplay.from']()}:
</span>
<div className="ml-3">
<span className="text-muted-foreground text-nowrap pr-1 font-bold">
{cleanNameDisplay(emailData?.sender?.name)}
</span>
{emailData?.sender?.name !== emailData?.sender?.email && (
<span className="text-muted-foreground text-nowrap">
{cleanEmailDisplay(emailData?.sender?.email)}
</span>
)}
</div>
</div>
<div className="flex">
<span className="w-24 text-nowrap text-end text-gray-500">
{m['common.mailDisplay.to']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{emailData?.to
?.map((t) => cleanEmailDisplay(t.email))
.join(', ')}
</span>
</div>
{emailData?.replyTo && emailData.replyTo.length > 0 && (
<div className="flex">
<span className="w-24 text-nowrap text-end text-gray-500">
{m['common.mailDisplay.replyTo']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{cleanEmailDisplay(emailData?.replyTo)}
</span>
</div>
)}
{emailData?.cc && emailData.cc.length > 0 && (
<div className="flex">
<span className="shrink-0text-nowrap w-24 text-end text-gray-500">
{m['common.mailDisplay.cc']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{emailData?.cc
?.map((t) => cleanEmailDisplay(t.email))
.join(', ')}
</span>
</div>
)}
{emailData?.bcc && emailData.bcc.length > 0 && (
<div className="flex">
<span className="w-24 text-end text-gray-500">
{m['common.mailDisplay.bcc']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{emailData?.bcc
?.map((t) => cleanEmailDisplay(t.email))
.join(', ')}
</span>
</div>
)}
<div className="flex">
<span className="w-24 text-end text-gray-500">
{m['common.mailDisplay.date']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{emailData?.receivedOn &&
!isNaN(new Date(emailData.receivedOn).getTime())
? format(new Date(emailData.receivedOn), 'PPpp')
: ''}
</span>
</div>
<div className="flex">
<span className="w-24 text-end text-gray-500">
{m['common.mailDisplay.mailedBy']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{cleanEmailDisplay(emailData?.sender?.email)}
</span>
</div>
<div className="flex">
<span className="w-24 text-end text-gray-500">
{m['common.mailDisplay.signedBy']()}:
</span>
<span className="text-muted-foreground ml-3 text-nowrap">
{cleanEmailDisplay(emailData?.sender?.email)}
</span>
</div>
{emailData.tls && (
<div className="flex items-center">
<span className="w-24 text-end text-gray-500">
{m['common.mailDisplay.security']()}:
</span>
<div className="text-muted-foreground ml-3 flex items-center gap-1">
<Lock className="h-4 w-4 text-green-600" />{' '}
{m['common.mailDisplay.standardEncryption']()}
</div>
</div>
)}
</div>
</PopoverContent>
</Popover>
</div>
<div className="flex items-center justify-center">
<div className="text-muted-foreground mr-2 flex flex-col !flex-nowrap items-end text-sm font-medium dark:text-[#8C8C8C]">
<time className="whitespace-nowrap">
{emailData?.receivedOn ? formatDate(emailData.receivedOn) : ''}
</time>
{shouldShowSeparateTime(emailData?.receivedOn) && (
<time className="whitespace-nowrap text-xs opacity-75">
{emailData?.receivedOn && formatTime(emailData.receivedOn)}
</time>
)}
</div>
{/* options menu */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}
className="inline-flex h-7 w-7 items-center justify-center gap-1 overflow-hidden rounded-md bg-white focus:outline-none focus:ring-0 dark:bg-[#313131]"
>
<ThreeDots className="fill-iconLight dark:fill-iconDark" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="bg-white dark:bg-[#313131]">
<DropdownMenuItem
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
printMail();
}}
>
<Printer className="fill-iconLight dark:fill-iconDark mr-2 h-4 w-4" />
{m['common.mailDisplay.print']()}
</DropdownMenuItem>
{(emailData.attachments?.length ?? 0) > 0 && (
<DropdownMenuItem
disabled={!emailData.attachments?.length}
className={
!emailData.attachments?.length
? 'data-[disabled]:pointer-events-auto'
: ''
}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handleDownloadAllAttachments(
emailData.subject || 'email',
emailData.attachments || [],
)();
}}
>
<HardDriveDownload className="fill-iconLight dark:text-iconDark dark:fill-iconLight mr-2 h-4 w-4" />
Download All Attachments
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div className="flex justify-between">
<div className="flex gap-1">
<p className="text-muted-foreground text-sm font-medium dark:text-[#8C8C8C]">
{m['common.mailDisplay.to']()}:{' '}
{(() => {
// Combine to and cc recipients
const allRecipients = [
...(emailData?.to || []),
...(emailData?.cc || []),
];
// If you're the only recipient
if (allRecipients.length === 1 && folder !== 'sent') {
return <span key="you">You</span>;
}
// Show first 3 recipients + count of others
const visibleRecipients = allRecipients.slice(0, 3);
const remainingCount = allRecipients.length - 3;
return (
<>
{visibleRecipients.map((recipient, index) => (
<span key={recipient.email}>
{cleanNameDisplay(recipient.name) ||
cleanEmailDisplay(recipient.email)}
{index < visibleRecipients.length - 1 ? ', ' : ''}
</span>
))}
{remainingCount > 0 && (
<span key="others">{`, +${remainingCount} others`}</span>
)}
</>
);
})()}
</p>
{(emailData?.bcc?.length || 0) > 0 && (
<p className="text-muted-foreground text-sm font-medium dark:text-[#8C8C8C]">
Bcc:{' '}
{emailData?.bcc?.map((recipient, index) => (
<span key={recipient.email}>
{cleanNameDisplay(recipient.name) ||
cleanEmailDisplay(recipient.email)}
{index < (emailData?.bcc?.length || 0) - 1 ? ', ' : ''}
</span>
))}
</p>
)}
</div>
</div>
</div>
{/* Pending, needs a storage to make the unsubscribe status consitent */}
{/* <span className="text-muted-foreground flex grow-0 items-center gap-2 text-sm">
{listUnsubscribeAction && (
<Dialog>
<DialogTrigger asChild>
<Button
size="xs"
variant="secondary"
disabled={unsubscribed || isUnsubscribing}
>
{unsubscribed && <Check className="h-4 w-4" />}
{isUnsubscribing && (
<LoaderCircleIcon className="h-4 w-4 animate-spin" />
)}
{unsubscribed
? t('common.mailDisplay.unsubscribed')
: t('common.mailDisplay.unsubscribe')}
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('common.mailDisplay.unsubscribe')}</DialogTitle>
<DialogDescription className="break-words">
{t('common.mailDisplay.unsubscribeDescription')}
</DialogDescription>
</DialogHeader>
<DialogFooter className="gap-2">
<DialogClose asChild>
<Button disabled={isUnsubscribing} variant="outline">
{t('common.mailDisplay.cancel')}
</Button>
</DialogClose>
<DialogClose asChild>
<Button disabled={isUnsubscribing} onClick={_handleUnsubscribe}>
{t('common.mailDisplay.unsubscribe')}
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)}
</span> */}
</div>
</div>
</div>
</div>
</div>
<div
className={cn(
'h-0 overflow-hidden transition-all duration-200',
!isCollapsed && 'h-[1px]',
)}
></div>
<div
className={cn(
'grid overflow-hidden transition-all duration-200',
isCollapsed ? 'grid-rows-[0fr]' : 'grid-rows-[1fr]',
)}
onClick={(e) => e.stopPropagation()}
>
<div className="min-h-0 overflow-hidden">
<div className="h-fit w-full p-0">
{/* mail main body */}
{emailData?.decodedBody ? (
<MailContent
id={emailData.id}
html={emailData?.decodedBody}
senderEmail={emailData.sender.email}
/>
) : null}
{/* mail attachments */}
{emailData?.attachments && emailData?.attachments.length > 0 ? (
<div className="mb-4 flex flex-wrap items-center gap-2 px-4 pt-4">
{emailData?.attachments.map((attachment, index) => (
<div key={index} className="flex">
<button
className="flex cursor-pointer items-center gap-1 rounded-[5px] bg-[#FAFAFA] px-1.5 py-1 text-sm font-medium hover:bg-[#F0F0F0] dark:bg-[#262626] dark:hover:bg-[#303030]"
onClick={() => openAttachment(attachment)}
>
{getFileIcon(attachment.filename)}
<span className="max-w-[15ch] truncate text-sm text-black dark:text-white">
{attachment.filename}
</span>{' '}
<span className="text-muted-foreground whitespace-nowrap text-sm dark:text-[#929292]">
{formatFileSize(attachment.size)}
</span>
</button>
<button
onClick={() => downloadAttachment(attachment)}
className="flex cursor-pointer items-center gap-1 rounded-[5px] px-1.5 py-1 text-sm"
>
<HardDriveDownload className="text-muted-foreground dark:text-muted-foreground h-4 w-4 fill-[#FAFAFA] dark:fill-[#262626]" />
</button>
{index < (emailData?.attachments?.length || 0) - 1 && (
<div className="m-auto h-2 w-[1px] bg-[#E0E0E0] dark:bg-[#424242]" />
)}
</div>
))}
</div>
) : null}
<div className="my-2.5 flex gap-2 px-4">
<ActionButton
onClick={(e) => {
e.stopPropagation();
setIsCollapsed(false);
setMode('reply');
setActiveReplyId(emailData.id);
}}
icon={<Reply className="fill-muted-foreground dark:fill-[#9B9B9B]" />}
text={m['common.mail.reply']()}
shortcut={isLastEmail ? 'r' : undefined}
/>
<ActionButton
onClick={(e) => {
e.stopPropagation();
setIsCollapsed(false);
setMode('replyAll');
setActiveReplyId(emailData.id);
}}
icon={<ReplyAll className="fill-muted-foreground dark:fill-[#9B9B9B]" />}
text={m['common.mail.replyAll']()}
shortcut={isLastEmail ? 'a' : undefined}
/>
<ActionButton
onClick={(e) => {
e.stopPropagation();
setIsCollapsed(false);
setMode('forward');
setActiveReplyId(emailData.id);
}}
icon={<Forward className="fill-muted-foreground dark:fill-[#9B9B9B]" />}
text={m['common.mail.forward']()}
shortcut={isLastEmail ? 'f' : undefined}
/>
</div>
</div>
</div>
</div>
</div>
</>

Fix in CursorFix in Web


Bug: Undefined Folder Parameter Causes Malformed URL

The handleOpenInNewTab function constructs a URL using the folder variable from useParams. If the current route does not include a folder parameter, folder will be undefined, leading to a malformed URL (e.g., /mail/undefined?threadId=...).

apps/mail/components/context/thread-context.tsx#L267-L270

const handleOpenInNewTab = () => {
window.open(`/mail/${folder}?threadId=${threadId}`, '_blank');
};

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

Copy link
Contributor

@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

🔭 Outside diff range comments (1)
apps/server/src/routes/ai.ts (1)

70-80: Fix variable declaration scope issue in switch statement.

The static analysis tool correctly identifies that variable declarations in switch clauses should be wrapped in blocks to prevent access from other clauses.

       case Tools.SendEmail:
+        {
         const result = await agent.create({
           to: body.to.map((to: any) => ({
             name: to.name ?? to.email,
             email: to.email ?? 'founders@0.email',
           })),
           subject: body.subject,
           message: body.message,
           attachments: [],
           headers: {},
         });
         return c.json({ success: true, result });
+        }
🧹 Nitpick comments (1)
apps/server/src/lib/driver/microsoft.ts (1)

1300-1302: LGTM! Consider adding documentation for the placeholder implementation.

The method correctly implements the interface requirement and maintains type safety. Since this returns an empty history array, consider adding a comment to clarify whether this is a temporary placeholder or if history functionality is not applicable for Microsoft Graph API.

+  // TODO: Implement history functionality for Microsoft Graph API or document if not applicable
   listHistory<T>(historyId: string): Promise<{ history: T[]; historyId: string }> {
     return Promise.resolve({ history: [], historyId });
   }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1c9c239 and 9ba3ac8.

📒 Files selected for processing (21)
  • apps/mail/components/context/thread-context.tsx (3 hunks)
  • apps/mail/components/create/create-email.tsx (4 hunks)
  • apps/mail/components/create/email-composer.tsx (1 hunks)
  • apps/mail/components/mail/mail-display.tsx (6 hunks)
  • apps/mail/components/mail/reply-composer.tsx (2 hunks)
  • apps/mail/components/ui/ai-sidebar.tsx (1 hunks)
  • apps/mail/components/ui/app-sidebar.tsx (2 hunks)
  • apps/mail/components/ui/nav-user.tsx (1 hunks)
  • apps/mail/messages/en.json (1 hunks)
  • apps/mail/providers/query-provider.tsx (1 hunks)
  • apps/server/src/lib/driver/google.ts (4 hunks)
  • apps/server/src/lib/driver/microsoft.ts (1 hunks)
  • apps/server/src/lib/driver/types.ts (1 hunks)
  • apps/server/src/lib/server-utils.ts (0 hunks)
  • apps/server/src/pipelines.effect.ts (1 hunks)
  • apps/server/src/routes/agent/tools.ts (2 hunks)
  • apps/server/src/routes/ai.ts (14 hunks)
  • apps/server/src/routes/chat.ts (13 hunks)
  • apps/server/src/services/mcp-service/mcp.ts (0 hunks)
  • apps/server/src/trpc/routes/ai/compose.ts (3 hunks)
  • apps/server/src/trpc/trpc.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/server/src/lib/server-utils.ts
  • apps/server/src/services/mcp-service/mcp.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/server/src/pipelines.effect.ts
  • apps/server/src/trpc/trpc.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • apps/server/src/trpc/routes/ai/compose.ts
  • apps/mail/components/create/email-composer.tsx
  • apps/mail/messages/en.json
  • apps/mail/components/ui/ai-sidebar.tsx
  • apps/mail/components/ui/app-sidebar.tsx
  • apps/mail/components/create/create-email.tsx
  • apps/mail/components/context/thread-context.tsx
  • apps/mail/components/ui/nav-user.tsx
  • apps/server/src/routes/agent/tools.ts
  • apps/server/src/lib/driver/google.ts
  • apps/server/src/routes/chat.ts
  • apps/mail/providers/query-provider.tsx
  • apps/mail/components/mail/reply-composer.tsx
  • apps/mail/components/mail/mail-display.tsx
🧰 Additional context used
🧠 Learnings (2)
apps/server/src/lib/driver/types.ts (1)
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:331-331
Timestamp: 2025-06-28T03:56:09.376Z
Learning: In apps/server/src/trpc/routes/mail.ts, the user indicated they are not using ISO format for the scheduleAt parameter, despite the frontend code showing toISOString() usage in the ScheduleSendPicker component.
apps/server/src/routes/ai.ts (1)
Learnt from: retrogtx
PR: Mail-0/Zero#1468
File: apps/server/src/trpc/routes/mail.ts:386-391
Timestamp: 2025-06-27T04:59:29.731Z
Learning: In apps/server/src/trpc/routes/mail.ts, the attachment processing logic conditionally handles mixed attachment types - it preserves existing File-like objects with arrayBuffer methods while only converting serialized attachments that need processing through toAttachmentFiles.
🪛 Biome (1.9.4)
apps/server/src/routes/ai.ts

[error] 70-79: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor BugBot
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
apps/server/src/lib/driver/types.ts (1)

28-28: LGTM! Improved type safety for draft handling.

The type change from string to string | null properly reflects the reality that draft messages may not have an internal date or that some email providers may return null for this field. This improves type safety and aligns with the broader draft handling improvements mentioned in the PR.

apps/server/src/routes/ai.ts (8)

4-4: LGTM! Appropriate import change for the agent-based refactoring.

The import change from driver utilities to getZeroAgent aligns with the systematic refactoring from driver-based to agent-based email operations.


42-42: LGTM! Consistent with the agent-based architecture.

The replacement of driver instantiation with agent instantiation is appropriate for this refactoring.


47-60: LGTM! Method name changes align with agent interface.

The changes from driver.list to agent.listThreads and driver.get to agent.getThread are consistent with the new agent-based architecture. The functionality and response handling remain intact.


147-147: LGTM! Consistent agent instantiation.

The replacement of driver with agent is consistent throughout the file.


193-199: LGTM! Agent method calls are correctly updated.

The method calls have been properly updated to use the agent interface with the new method names.


232-232: LGTM! Consistent agent method usage.

The getThread method call is correctly updated to use the agent interface.


265-265: LGTM! Consistent modifyLabels method usage.

All modifyLabels calls have been correctly updated to use the agent interface while maintaining the same functionality.

Also applies to: 284-284, 305-305, 426-426, 458-458


338-338: LGTM! Label management methods correctly updated.

The label management methods (getUserLabels, getLabel, createLabel) have been consistently updated to use the agent interface.

Also applies to: 359-359, 385-385

Copy link
Collaborator Author

MrgSub commented Jul 9, 2025

Merge activity

  • Jul 9, 10:58 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 9, 10:58 PM UTC: @MrgSub merged this pull request with Graphite.

@MrgSub MrgSub merged commit 7f0893b into staging Jul 9, 2025
8 checks passed
@MrgSub MrgSub deleted the 07-09-all_round_fixes branch July 9, 2025 22:58
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.

1 participant