Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
36d9b47
separate vectorized dbs (#1637)
MrgSub Jul 5, 2025
38bcc6e
Enable voice (#1639)
MrgSub Jul 5, 2025
8a17b37
fixes to email rendering (#1646)
ahmetskilinc Jul 7, 2025
307f1de
fix: allow <style> and essential attributes in sanitizer (#1648)
retrogtx Jul 7, 2025
e8dd809
frontend cleanup (#1662)
ahmetskilinc Jul 7, 2025
0ba8c97
performance (#1663)
MrgSub Jul 7, 2025
d18de78
minor fix (#1664)
MrgSub Jul 7, 2025
af19315
SyncStuff (#1657)
MrgSub Jul 7, 2025
b82aa28
Fixing Notifications (#1667)
MrgSub Jul 7, 2025
ddb4fed
Hotfix (#1668)
MrgSub Jul 7, 2025
d0113d0
billing hotfix (#1670)
MrgSub Jul 8, 2025
ceb3d6d
fixes labels in sidebar (#1674)
ahmetskilinc Jul 8, 2025
c3ec592
feat: add quote text, remove risky style tag (#1672)
retrogtx Jul 8, 2025
9b6b3c6
Drop not (#1676)
MrgSub Jul 8, 2025
e8b61ad
better checbox in mail list (#1677)
ahmetskilinc Jul 8, 2025
8abb739
fix reply close button (#1678)
MrgSub Jul 8, 2025
fce8f97
fix agent rpc creation (#1679)
MrgSub Jul 8, 2025
662d521
Hotfix errors (#1680)
MrgSub Jul 8, 2025
267620a
better logs (#1682)
MrgSub Jul 8, 2025
17b246e
sync all folders (#1683)
MrgSub Jul 8, 2025
99bdd71
little-cache (#1684)
MrgSub Jul 8, 2025
2b1e400
even less cache (#1686)
MrgSub Jul 8, 2025
7fec116
categories (#1689)
MrgSub Jul 8, 2025
16c3f95
disable workflows (#1690)
MrgSub Jul 9, 2025
7cc9838
refactor: perf improvements to opening emails, tanstack query caching…
izadoesdev Jul 9, 2025
a48775d
refactor pipelines (#1696)
MrgSub Jul 9, 2025
c7b637f
Merge branch 'main' into staging
MrgSub Jul 9, 2025
7f0893b
all round fixes (#1697)
MrgSub Jul 9, 2025
3532012
use bimi records to get logos (#1681)
ahmetskilinc Jul 9, 2025
277f476
cleanup on isle zero (#1699)
MrgSub Jul 10, 2025
0030a98
feat: update translations via @LingoDotDev (#1698)
github-actions[bot] Jul 10, 2025
6020305
hotfix stuff (#1703)
MrgSub Jul 10, 2025
7bca596
minor fix (#1704)
MrgSub Jul 10, 2025
00b9d8c
disable workflows (#1705)
MrgSub Jul 10, 2025
c357fbf
delay (#1706)
MrgSub Jul 10, 2025
5cb4511
Sync delay (#1707)
MrgSub Jul 11, 2025
699d8f2
hotfix deployment (#1708)
MrgSub Jul 11, 2025
5d74211
no migration (#1709)
MrgSub Jul 11, 2025
992dc80
fix deployment (#1712)
MrgSub Jul 11, 2025
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: 2 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Thank you for your interest in contributing to 0.email! We're excited to have yo
- Make sure the app runs without errors
- Test your feature thoroughly

- Please lint using `pnpm dlx oxlint@latest` or by downloading an IDE extension here: https://oxc.rs/docs/guide/usage/linter.html#vscode-extension

5. **Commit Your Changes**

- Use clear, descriptive commit messages
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ jobs:

- name: Install dependencies 📦
run: pnpm install

- name: Lint JS
run: pnpm dlx oxlint@latest --deny-warnings
18 changes: 18 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"plugins": ["react", "unicorn", "typescript", "oxc"],
"rules": {
"no-alert": "error", // Emit an error message when a call to `alert()` is found
"oxc/approx-constant": "warn", // Show a warning when you write a number close to a known constant
"no-plusplus": "off", // Allow using the `++` and `--` operators
"no-useless-call": "error",
"no-accumulating-spread": "error",
"no-array-index-key": "error",
"jsx-no-jsx-as-prop": "error",
"jsx-no-new-array-as-prop": "error",
"jsx-no-new-function-as-prop": "error",
"jsx-no-new-object-as-prop": "error",
"prefer-array-find": "error",
"prefer-set-has": "error",
"exhaustive-deps": "off"
}
}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ You can set up Zero in two ways:
- Go to the [Twilio](https://www.twilio.com/)
- Create a Twilio account if you don’t already have one
- From the dashboard, locate your:

- Account SID
- Auth Token
- Phone Number
Expand Down Expand Up @@ -265,6 +266,15 @@ Zero uses PostgreSQL for storing data. Here's how to set it up:
```
> If you run `pnpm dev` in your terminal, the studio command should be automatically running with the app.

### Sync

Background: https://x.com/cmdhaus/status/1940886269950902362
We're now storing the user's emails in their Durable Object & an R2 bucket. This allow us to speed things up, a lot.
This also introduces 3 environment variables, `DROP_AGENT_TABLES`,`THREAD_SYNC_MAX_COUNT`, `THREAD_SYNC_LOOP`.
`DROP_AGENT_TABLES`: should the durable object drop the threads table before starting a sync
`THREAD_SYNC_MAX_COUNT`: how many threads should we sync? max `500` because it's using the same number for the maxResults number from the driver. i.e 500 results per page.
`THREAD_SYNC_LOOP`: should make sure to sync all of the items inside a folder? i.e if THREAD_SYNC_MAX_COUNT=500 it will sync 500 threads per request until the folder is fully synced. (should be true in production)

## Contribute

Please refer to the [contributing guide](.github/CONTRIBUTING.md).
Expand Down
13 changes: 7 additions & 6 deletions apps/mail/app/(full-width)/contributors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
ChartAreaIcon,
GitPullRequest,
LayoutGrid,
FileCode,
} from 'lucide-react';
import {
Area,
Expand Down Expand Up @@ -52,13 +51,13 @@ interface ActivityData {
pullRequests: number;
}

const excludedUsernames = [
const excludedUsernames = new Set([
'bot1',
'dependabot',
'github-actions',
'zerodotemail',
'autofix-ci[bot]',
];
]);
const coreTeamMembers = [
'nizzyabi',
'ahmetskilinc',
Expand Down Expand Up @@ -142,7 +141,7 @@ export default function OpenPage() {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [allContributors, setAllContributors] = useState<Contributor[]>([]);
const [isRendered, setIsRendered] = useState(false);
const [, setIsRendered] = useState(false);

useEffect(() => setIsRendered(true), []);

Expand Down Expand Up @@ -199,7 +198,7 @@ export default function OpenPage() {
return allContributors
?.filter(
(contributor) =>
!excludedUsernames.includes(contributor.login) &&
!excludedUsernames.has(contributor.login) &&
coreTeamMembers.some(
(member) => member.toLowerCase() === contributor.login.toLowerCase(),
),
Expand All @@ -216,7 +215,7 @@ export default function OpenPage() {
allContributors
?.filter(
(contributor) =>
!excludedUsernames.includes(contributor.login) &&
!excludedUsernames.has(contributor.login) &&
!coreTeamMembers.some(
(member) => member.toLowerCase() === contributor.login.toLowerCase(),
),
Expand Down Expand Up @@ -1011,6 +1010,7 @@ export default function OpenPage() {
<a
href="https://discord.gg/mail0"
target="_blank"
rel="noreferrer"
className="text-neutral-500 transition-colors hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200"
aria-label="Join our Discord"
>
Expand All @@ -1019,6 +1019,7 @@ export default function OpenPage() {
<a
href="https://x.com/mail0dotcom"
target="_blank"
rel="noreferrer"
className="text-neutral-500 transition-colors hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200"
aria-label="Follow us on X (Twitter)"
>
Expand Down
4 changes: 2 additions & 2 deletions apps/mail/app/(full-width)/hr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { formatInTimeZone, fromZonedTime, toZonedTime } from 'date-fns-tz';
import { getBrowserTimezone } from '@/lib/timezones';

import { Plus, Trash2, Clock } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
Expand Down Expand Up @@ -145,7 +145,7 @@ export default function HRPage() {
},
]);
// Company timezone
const [userTimezone, setUserTimezone] = useState('America/Los_Angeles');
const [userTimezone] = useState('America/Los_Angeles');
const [userWorkingHours, setUserWorkingHours] = useState<WorkingHours>({
startTime: '09:00',
endTime: '17:00',
Expand Down
69 changes: 2 additions & 67 deletions apps/mail/app/(full-width)/pricing.tsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,12 @@
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
NavigationMenuContent,
ListItem,
} from '@/components/ui/navigation-menu';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
import { PixelatedBackground } from '@/components/home/pixelated-bg';
import PricingCard from '@/components/pricing/pricing-card';
import Comparision from '@/components/pricing/comparision';
import { signIn, useSession } from '@/lib/auth-client';
import { Separator } from '@/components/ui/separator';
import { Navigation } from '@/components/navigation';
import { useBilling } from '@/hooks/use-billing';
import { Link, useNavigate } from 'react-router';
import { Button } from '@/components/ui/button';
import Footer from '@/components/home/footer';
import { useState, useMemo } from 'react';
import { Menu } from 'lucide-react';
import { toast } from 'sonner';

const resources = [
{
title: 'GitHub',
href: 'https://github.com/Mail-0/Zero',
description: 'Check out our open-source projects and contributions.',
platform: 'github' as const,
},
{
title: 'Twitter',
href: 'https://x.com/mail0dotcom',
description: 'Follow us for the latest updates and announcements.',
platform: 'twitter' as const,
},
{
title: 'LinkedIn',
href: 'https://www.linkedin.com/company/mail0/',
description: 'Connect with us professionally and stay updated.',
platform: 'linkedin' as const,
},
{
title: 'Discord',
href: 'https://discord.gg/mail0',
description: 'Join our community and chat with the team.',
platform: 'discord' as const,
},
];
import { Navigation } from '@/components/navigation';

const aboutLinks = [
{
title: 'About',
href: '/about',
description: 'Learn more about Zero and our mission.',
},
{
title: 'Privacy',
href: '/privacy',
description: 'Read our privacy policy and data handling practices.',
},
{
title: 'Terms of Service',
href: '/terms',
description: 'Review our terms of service and usage guidelines.',
},
];
import Footer from '@/components/home/footer';

export default function PricingPage() {
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const { data: session } = useSession();

return (
<main className="relative flex min-h-screen flex-1 flex-col overflow-x-hidden bg-[#0F0F0F]">
<PixelatedBackground
Expand Down
6 changes: 3 additions & 3 deletions apps/mail/app/(full-width)/privacy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { Navigation } from '@/components/navigation';
import { Button } from '@/components/ui/button';
import Footer from '@/components/home/footer';
import { createSectionId } from '@/lib/utils';
import { useNavigate } from 'react-router';
import { toast } from 'sonner';


import React from 'react';

const LAST_UPDATED = 'May 16, 2025';

export default function PrivacyPolicy() {
const navigate = useNavigate();

const { copiedValue: copiedSection, copyToClipboard } = useCopyToClipboard();

const handleCopyLink = (sectionId: string) => {
Expand Down
6 changes: 3 additions & 3 deletions apps/mail/app/(full-width)/terms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { Navigation } from '@/components/navigation';
import { Button } from '@/components/ui/button';
import Footer from '@/components/home/footer';
import { createSectionId } from '@/lib/utils';
import { useNavigate } from 'react-router';
import { toast } from 'sonner';


import React from 'react';

const LAST_UPDATED = 'February 13, 2025';

export default function TermsOfService() {
const { copiedValue: copiedSection, copyToClipboard } = useCopyToClipboard();
const navigate = useNavigate();


const handleCopyLink = (sectionId: string) => {
const url = `${window.location.origin}${window.location.pathname}#${sectionId}`;
Expand Down
4 changes: 1 addition & 3 deletions apps/mail/app/(routes)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { HotkeyProviderWrapper } from '@/components/providers/hotkey-provider-wrapper';
import { CommandPaletteProvider } from '@/components/context/command-palette-context';
import { VoiceProvider } from '@/providers/voice-provider';

import { Outlet } from 'react-router';

export default function Layout() {
return (
<CommandPaletteProvider>
{/* <VoiceProvider> */}
<HotkeyProviderWrapper>
<div className="relative flex max-h-screen w-full overflow-hidden">
<Outlet />
</div>
</HotkeyProviderWrapper>
{/* </VoiceProvider> */}
</CommandPaletteProvider>
);
}
8 changes: 4 additions & 4 deletions apps/mail/app/(routes)/mail/[folder]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useLoaderData, useNavigate } from 'react-router';
import { useTRPC } from '@/providers/query-provider';

import { MailLayout } from '@/components/mail/mail';
import { useLabels } from '@/hooks/use-labels';
import { authProxy } from '@/lib/auth-proxy';
import { useEffect, useState } from 'react';
import type { Route } from './+types/page';

const ALLOWED_FOLDERS = ['inbox', 'draft', 'sent', 'spam', 'bin', 'archive'];
const ALLOWED_FOLDERS = new Set(['inbox', 'draft', 'sent', 'spam', 'bin', 'archive']);

export async function clientLoader({ params, request }: Route.ClientLoaderArgs) {
if (!params.folder) return Response.redirect(`${import.meta.env.VITE_PUBLIC_APP_URL}/mail/inbox`);
Expand All @@ -24,9 +24,9 @@ export default function MailPage() {
const navigate = useNavigate();
const [isLabelValid, setIsLabelValid] = useState<boolean | null>(true);

const isStandardFolder = ALLOWED_FOLDERS.includes(folder);
const isStandardFolder = ALLOWED_FOLDERS.has(folder);

const { data: userLabels, isLoading: isLoadingLabels } = useLabels();
const { userLabels, isLoading: isLoadingLabels } = useLabels();

useEffect(() => {
if (isStandardFolder) {
Expand Down
8 changes: 3 additions & 5 deletions apps/mail/app/(routes)/mail/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { HotkeyProviderWrapper } from '@/components/providers/hotkey-provider-wrapper';
import { OnboardingWrapper } from '@/components/onboarding';
import { VoiceProvider } from '@/providers/voice-provider';

import { NotificationProvider } from '@/components/party';
import { AppSidebar } from '@/components/ui/app-sidebar';
import { Outlet, useLoaderData } from 'react-router';
import type { Route } from './+types/layout';
import { Outlet, } from 'react-router';


export default function MailLayout() {
return (
// <VoiceProvider>
<HotkeyProviderWrapper>
<AppSidebar />
<div className="bg-sidebar dark:bg-sidebar w-full">
Expand All @@ -17,6 +16,5 @@ export default function MailLayout() {
<OnboardingWrapper />
<NotificationProvider />
</HotkeyProviderWrapper>
// </VoiceProvider>
);
}
2 changes: 1 addition & 1 deletion apps/mail/app/(routes)/settings/appearance/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export default function AppearancePage() {
<FormField
control={form.control}
name="colorTheme"
render={({ field }) => (
render={() => (
<FormItem>
<FormLabel>{m['pages.settings.appearance.theme']()}</FormLabel>
<FormControl>
Expand Down
9 changes: 2 additions & 7 deletions apps/mail/app/(routes)/settings/categories/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@ import { useTRPC } from '@/providers/query-provider';
import { toast } from 'sonner';
import type { CategorySetting } from '@/hooks/use-categories';
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover';
import * as Icons from '@/components/icons/icons';

import { Sparkles } from '@/components/icons/icons';
import { Loader, GripVertical } from 'lucide-react';
import {
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from '@/components/ui/select';
} from '@/components/ui/select';
import { Badge } from '@/components/ui/badge';
import {
DndContext,
Expand Down
6 changes: 3 additions & 3 deletions apps/mail/app/(routes)/settings/connections/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { SettingsCard } from '@/components/settings/settings-card';
import { AddConnectionDialog } from '@/components/connection/add';
import { PricingDialog } from '@/components/ui/pricing-dialog';

import { useSession, authClient } from '@/lib/auth-client';
import { useConnections } from '@/hooks/use-connections';
import { useTRPC } from '@/providers/query-provider';
Expand Down Expand Up @@ -62,9 +62,9 @@ export default function ConnectionsPage() {
<div className="space-y-6">
{isLoading ? (
<div className="grid gap-4 md:grid-cols-3">
{[...Array(3)].map((_, i) => (
{[...Array(3)].map((n) => (
<div
key={i}
key={n}
className="bg-popover flex items-center justify-between rounded-lg border p-4"
>
<div className="flex min-w-0 items-center gap-4">
Expand Down
4 changes: 2 additions & 2 deletions apps/mail/app/(routes)/settings/danger-zone/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useMutation } from '@tanstack/react-query';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { AlertTriangle } from 'lucide-react';
import { useNavigate } from 'react-router';

import { useForm } from 'react-hook-form';
import { m } from '@/paraglide/messages';
import { clear } from 'idb-keyval';
Expand All @@ -33,7 +33,7 @@ const formSchema = z.object({

function DeleteAccountDialog() {
const [isOpen, setIsOpen] = useState(false);
const navigate = useNavigate();

const trpc = useTRPC();
const { refetch } = useSession();
const { mutateAsync: deleteAccount, isPending } = useMutation(trpc.user.delete.mutationOptions());
Expand Down
Loading