Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/mail/app/(routes)/settings/connections/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export default function ConnectionsPage() {
<Trash className="h-4 w-4" />
</Button>
</DialogTrigger>
<DialogContent showOverlay>
<DialogContent>
<DialogHeader>
<DialogTitle>
{m['pages.settings.connections.disconnectTitle']()}
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/app/(routes)/settings/danger-zone/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function DeleteAccountDialog() {
<DialogTrigger asChild>
<Button variant="destructive">{m['pages.settings.dangerZone.deleteAccount']()}</Button>
</DialogTrigger>
<DialogContent showOverlay>
<DialogContent>
<DialogHeader>
<DialogTitle>{m['pages.settings.dangerZone.title']()}</DialogTitle>
<DialogDescription>{m['pages.settings.dangerZone.description']()}</DialogDescription>
Expand Down
22 changes: 17 additions & 5 deletions apps/mail/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
--subtleBlack: #1F1F1F;
--skyBlue: #0066FF;
--shinyGray: #A1A1A1;
--sidebar: hsl(0 0% 98%);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unify the sidebar token — single source of truth.

You introduced --sidebar but the theme still maps to --sidebar-background. Wire it up or delete the new var to avoid dead config.

Apply this diff to use the new token across the theme:

@@
-  --sidebar: hsl(0 0% 98%);
+  --sidebar: hsl(0 0% 98%);
@@
-  --sidebar: hsl(240 5.9% 10%);
+  --sidebar: hsl(240 5.9% 10%);
@@
-  --color-sidebar: var(--sidebar-background);
+  --color-sidebar: var(--sidebar);

If other files still consume --sidebar-background, keep both and plan a follow-up sweep.

Also applies to: 118-118, 166-174

🤖 Prompt for AI Agents
In apps/mail/app/globals.css around lines 80 (and also check 118 and 166-174),
you added a new CSS variable --sidebar but the rest of the theme still uses
--sidebar-background, creating a dead/duplicate token; either wire the theme to
use --sidebar everywhere or remove --sidebar. Fix by updating all occurrences of
--sidebar-background in this file (and any other project files) to reference
--sidebar (or add --sidebar-background as an alias that references --sidebar) so
there is a single source of truth, and if you choose to keep both add a TODO to
sweep other files later.

}

.dark {
Expand Down Expand Up @@ -114,6 +115,7 @@
--sidebar-border: hsl(240 3.7% 15.9%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--panel: hsl(240 3.7% 10.2%);
--sidebar: hsl(240 5.9% 10%);
}

@theme inline {
Expand All @@ -137,14 +139,14 @@
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);

/* Chart colors */
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);

/* Static colors */
--color-darkBackground: var(--darkBackground);
--color-lightBackground: var(--lightBackground);
Expand All @@ -160,7 +162,7 @@
--color-subtleBlack: var(--subtleBlack);
--color-skyBlue: var(--skyBlue);
--color-shinyGray: var(--shinyGray);

/* sidebar colors */
--color-sidebar: var(--sidebar-background);
--color-sidebar-foreground: var(--sidebar-foreground);
Expand All @@ -170,7 +172,7 @@
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);

/* Border radius */
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
Expand Down Expand Up @@ -234,7 +236,6 @@
--animate-blink: blink 0.8s ease-in-out infinite;
}


/* Add scrollbar styling */
@utility style-scrollbar {
@apply scrollbar scrollbar-w-0 scrollbar-thumb-accent/40 scrollbar-track-transparent hover:scrollbar-thumb-accent scrollbar-thumb-rounded-full;
Expand Down Expand Up @@ -473,3 +474,14 @@
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
/*
---break---
*/
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
Comment on lines +480 to +487
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Kill the duplication: two base layers fight; last one wins.

You now have two @layer base blocks for * and body. This creates conflicting cascade. Pick one baseline and delete the other. If the intent is the simplified body, remove the earlier block.

Apply this diff to consolidate:

@@
-@layer base {
-  * {
-    @apply border-border;
-  }
-  body {
-    @apply bg-background text-foreground font-sans overscroll-none style-scrollbar;
-  }
-}

Optionally, if you still want the scrollbar styling and font family, fold them into the final block instead:

 @layer base {
   * {
-    @apply border-border outline-ring/50;
+    @apply border-border outline-ring/50;
   }
   body {
-    @apply bg-background text-foreground;
+    @apply bg-background text-foreground font-sans style-scrollbar;
+    /* If you intended to drop overscroll-none, omit it; else add it back explicitly */
+    /* @apply overscroll-none; */
   }
 }

Also sanity-check outline-ring/50 on * — global outline color is fine, but unnecessary work. Consider scoping to focusable elements if perf or specificity becomes noisy.


🏁 Script executed:

#!/bin/bash
echo "Occurrences of '@layer base' in apps/mail/app/globals.css:"
rg -n '@layer base' apps/mail/app/globals.css
echo ""
echo "Snippets around each occurrence:"
for L in $(rg -n '@layer base' apps/mail/app/globals.css | cut -d: -f1); do
  echo "=== Snippet around line $L ==="
  sed -n "$((L-5)),$((L+10))p" apps/mail/app/globals.css
  echo ""
done

Length of output: 1199


Consolidate duplicate @layer base blocks (lines 278 & 480). Only the last block wins, so remove the one at 278 or merge its unique body styles (font-sans overscroll-none style-scrollbar) into the block at 480. Consider scoping outline-ring/50 to focusable elements for better performance.

🤖 Prompt for AI Agents
In apps/mail/app/globals.css around lines 480 (and duplicate at 278), there are
two @layer base blocks where only the last one wins; remove the earlier block at
line 278 or merge its unique declarations (font-sans, overscroll-none,
style-scrollbar) into the block at 480 so all base styles are consolidated; also
scope the universal outline-rule (outline-ring/50) to focusable elements (e.g.,
links, buttons, inputs, textareas, selects, and elements using tabindex) instead
of applying to * to reduce styling cost and avoid unintended outlines.

2 changes: 1 addition & 1 deletion apps/mail/components/connection/add.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const AddConnectionDialog = ({
</Button>
)}
</DialogTrigger>
<DialogContent showOverlay={true}>
<DialogContent>
<DialogHeader>
<DialogTitle>{m['pages.settings.connections.connectEmail']()}</DialogTitle>
<DialogDescription>
Expand Down
20 changes: 10 additions & 10 deletions apps/mail/components/context/command-palette-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { format, subDays } from 'date-fns';
import { VisuallyHidden } from 'radix-ui';
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
import { m } from '@/paraglide/messages';
import { Pencil2 } from '../icons/icons';
import { Button } from '../ui/button';
Expand All @@ -65,7 +65,7 @@ type CommandPaletteContext = {
clearAllFilters: () => void;
};

interface CommandItem {
interface CommandItemType {
title: string;
icon?: ComponentType<{ size?: number; strokeWidth?: number; className?: string }>;
url?: string;
Expand Down Expand Up @@ -644,13 +644,13 @@ export function CommandPalette({ children }: { children: React.ReactNode }) {
const allCommands = useMemo(() => {
type CommandGroup = {
group: string;
items: CommandItem[];
items: CommandItemType[];
};

const searchCommands: CommandItem[] = [];
const mailCommands: CommandItem[] = [];
const settingsCommands: CommandItem[] = [];
const otherCommands: Record<string, CommandItem[]> = {};
const searchCommands: CommandItemType[] = [];
const mailCommands: CommandItemType[] = [];
const settingsCommands: CommandItemType[] = [];
const otherCommands: Record<string, CommandItemType[]> = {};

mailCommands.push({
title: 'Compose Email',
Expand Down Expand Up @@ -713,7 +713,7 @@ export function CommandPalette({ children }: { children: React.ReactNode }) {
section?.sections.forEach((group) => {
group.items.forEach((navItem) => {
if (navItem.disabled) return;
const item: CommandItem = {
const item: CommandItemType = {
title: navItem.title,
icon: navItem.icon,
url: navItem.url,
Expand Down Expand Up @@ -1885,10 +1885,10 @@ export function CommandPalette({ children }: { children: React.ReactNode }) {
setOpen(isOpen ? 'true' : null);
}}
>
<VisuallyHidden.VisuallyHidden>
<VisuallyHidden>
<DialogTitle>{m['common.commandPalette.title']()}</DialogTitle>
<DialogDescription>{m['common.commandPalette.description']()}</DialogDescription>
</VisuallyHidden.VisuallyHidden>
</VisuallyHidden>
{renderView()}
</CommandDialog>
{children}
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/components/context/label-sidebar-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function LabelSidebarContextMenu({ children, labelId, hide }: LabelSideba
</ContextMenu>

<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<DialogContent showOverlay={true}>
<DialogContent>
<DialogHeader>
<DialogTitle>{m['common.labels.deleteLabelConfirm']()}</DialogTitle>
<DialogDescription>
Expand Down
114 changes: 41 additions & 73 deletions apps/mail/components/create/create-email.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useUndoSend, type EmailData, deserializeFiles } from '@/hooks/use-undo-send';
import { useActiveConnection } from '@/hooks/use-connections';
import { Dialog, DialogClose } from '@/components/ui/dialog';
import { useEmailAliases } from '@/hooks/use-email-aliases';
import { cleanEmailAddresses } from '@/lib/email-utils';

Expand All @@ -15,7 +14,6 @@ import { useEffect, useMemo, useState } from 'react';

import type { Attachment } from '@/types';
import { useQueryState } from 'nuqs';
import { X } from '../icons/icons';
import posthog from 'posthog-js';
import { toast } from 'sonner';
import './prosemirror.css';
Expand Down Expand Up @@ -176,14 +174,6 @@ export function CreateEmail({
// Cast draft to our extended type that includes CC and BCC
const typedDraft = draft as unknown as DraftType;

const handleDialogClose = (open: boolean) => {
setIsComposeOpen(open ? 'true' : null);
if (!open) {
setDraftId(null);
clearUndoData();
}
};

const base64ToFile = (base64: string, filename: string, mimeType: string): File | null => {
try {
const byteString = atob(base64);
Expand All @@ -204,70 +194,48 @@ export function CreateEmail({
.filter((file): file is File => file !== null);

return (
<>
<Dialog open={!!isComposeOpen} onOpenChange={handleDialogClose}>
<div className="flex min-h-screen flex-col items-center justify-center gap-1">
<div className="flex w-[750px] justify-start">
<DialogClose asChild className="flex">
<button className="dark:bg-panelDark flex items-center gap-1 rounded-lg bg-[#F0F0F0] px-2 py-1 hover:bg-gray-100 dark:hover:bg-[#404040] transition-colors cursor-pointer">
<X className="fill-muted-foreground mt-0.5 h-3.5 w-3.5 dark:fill-[#929292]" />
<span className="text-muted-foreground text-sm font-medium dark:text-white">
esc
</span>
</button>
</DialogClose>
<div className="min-w-screen fixed left-1/2 top-1/2 flex min-h-screen -translate-x-1/2 -translate-y-1/2 items-center justify-center">
{isDraftLoading ? (
<div className="flex h-[600px] w-full items-center justify-center rounded-2xl border">
<div className="text-center">
<div className="mx-auto mb-4 h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600"></div>
<p>Loading draft...</p>
</div>
{isDraftLoading ? (
<div className="flex h-[600px] w-[750px] items-center justify-center rounded-2xl border">
<div className="text-center">
<div className="mx-auto mb-4 h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600"></div>
<p>Loading draft...</p>
</div>
</div>
) : (
<EmailComposer
key={typedDraft?.id || undoEmailData?.to?.join(',') || 'composer'}
className="mb-12 rounded-2xl border"
onSendEmail={handleSendEmail}
initialMessage={
undoEmailData?.message ||
typedDraft?.content ||
initialBody
}
initialTo={
undoEmailData?.to ||
typedDraft?.to?.map((e: string) => e.replace(/[<>]/g, '')) ||
processInitialEmails(initialTo)
}
initialCc={
undoEmailData?.cc ||
typedDraft?.cc?.map((e: string) => e.replace(/[<>]/g, '')) ||
processInitialEmails(initialCc)
}
initialBcc={
undoEmailData?.bcc ||
typedDraft?.bcc?.map((e: string) => e.replace(/[<>]/g, '')) ||
processInitialEmails(initialBcc)
}
onClose={() => {
setThreadId(null);
setActiveReplyId(null);
setIsComposeOpen(null);
setDraftId(null);
clearUndoData();
}}
initialAttachments={undoEmailData?.attachments || files}
initialSubject={
undoEmailData?.subject ||
typedDraft?.subject ||
initialSubject
}
autofocus={false}
settingsLoading={settingsLoading}
/>
)}
</div>
</Dialog>
</>
) : (
<EmailComposer
key={typedDraft?.id || undoEmailData?.to?.join(',') || 'composer'}
className="mb-12 w-full rounded-2xl border"
onSendEmail={handleSendEmail}
initialMessage={undoEmailData?.message || typedDraft?.content || initialBody}
initialTo={
undoEmailData?.to ||
typedDraft?.to?.map((e: string) => e.replace(/[<>]/g, '')) ||
processInitialEmails(initialTo)
}
initialCc={
undoEmailData?.cc ||
typedDraft?.cc?.map((e: string) => e.replace(/[<>]/g, '')) ||
processInitialEmails(initialCc)
}
initialBcc={
undoEmailData?.bcc ||
typedDraft?.bcc?.map((e: string) => e.replace(/[<>]/g, '')) ||
processInitialEmails(initialBcc)
}
onClose={() => {
setThreadId(null);
setActiveReplyId(null);
setIsComposeOpen(null);
setDraftId(null);
clearUndoData();
}}
initialAttachments={undoEmailData?.attachments || files}
initialSubject={undoEmailData?.subject || typedDraft?.subject || initialSubject}
autofocus={false}
settingsLoading={settingsLoading}
/>
)}
</div>
);
}
25 changes: 16 additions & 9 deletions apps/mail/components/create/email-composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -648,13 +648,20 @@ export function EmailComposer({
<span>Bcc</span>
</button>
{onClose && (
<button
tabIndex={-1}
className="flex h-full items-center gap-2 text-sm font-medium text-[#8C8C8C] hover:text-[#A8A8A8] hover:bg-gray-50 dark:hover:bg-[#404040] transition-colors cursor-pointer rounded-sm px-1 py-0.5"
onClick={handleClose}
>
<X className="h-3.5 w-3.5 fill-[#9A9A9A]" />
</button>
<Tooltip>
<TooltipTrigger asChild>
<button
tabIndex={-1}
className="flex h-full items-center gap-2 text-sm font-medium text-[#8C8C8C] hover:text-[#A8A8A8] hover:bg-gray-50 dark:hover:bg-[#404040] transition-colors cursor-pointer rounded-sm px-1 py-0.5"
onClick={handleClose}
>
<X className="h-3.5 w-3.5 fill-[#9A9A9A]" />
</button>
</TooltipTrigger>
<TooltipContent>
<p>Close (ESC)</p>
</TooltipContent>
</Tooltip>
)}
</div>
</div>
Expand Down Expand Up @@ -1005,7 +1012,7 @@ export function EmailComposer({
</div>

<Dialog open={showLeaveConfirmation} onOpenChange={setShowLeaveConfirmation}>
<DialogContent showOverlay className="z-99999 sm:max-w-[425px]">
<DialogContent className="z-99999 sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Discard message?</DialogTitle>
<DialogDescription>
Expand All @@ -1025,7 +1032,7 @@ export function EmailComposer({
</Dialog>

<Dialog open={showAttachmentWarning} onOpenChange={setShowAttachmentWarning}>
<DialogContent showOverlay className="z-99999 sm:max-w-[425px]">
<DialogContent className="z-99999 sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Attachment Warning</DialogTitle>
<DialogDescription>
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/components/create/template-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const TemplateButtonComponent: React.FC<TemplateButtonProps> = ({
</DropdownMenu>

<Dialog open={saveDialogOpen} onOpenChange={setSaveDialogOpen}>
<DialogContent showOverlay>
<DialogContent>
<DialogHeader>
<DialogTitle>Save as Template</DialogTitle>
</DialogHeader>
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/components/labels/label-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function LabelDialog({
return (
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
{trigger && <DialogTrigger asChild>{trigger}</DialogTrigger>}
<DialogContent showOverlay={true}>
<DialogContent>
<DialogHeader>
<DialogTitle>
{editingLabel ? m['common.labels.editLabel']() : m['common.mail.createNewLabel']()}
Expand Down
2 changes: 1 addition & 1 deletion apps/mail/components/magicui/file-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { ScrollArea } from '@/components/ui/scroll-area';
import { Button } from '@/components/ui/button';
import { FileIcon } from 'lucide-react';
import { Accordion } from 'radix-ui';
import * as Accordion from '@radix-ui/react-accordion';
import { cn } from '@/lib/utils';

type TreeViewElement = {
Expand Down
Loading
Loading