diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index d48a46505..e95fc1503 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -28,8 +28,9 @@ RUN apt-get update && apt-get install -y zsh
# install Oh My Zsh
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
-# Bun
+# Bun and pnpm installation
RUN curl -fsSL https://bun.sh/install | bash
+RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.bashrc" SHELL="$(which bash)" bash -
# Register Bun in bashrc and zshrc
RUN echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.bashrc
diff --git a/.gitignore b/.gitignore
index 3aaa9a60f..a7384153c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,4 +53,5 @@ apps/mail/scripts.ts
worker-configuration.d.ts
.dev.vars.*
-.react-router
\ No newline at end of file
+.react-router
+.pnpm-store
\ No newline at end of file
diff --git a/README.md b/README.md
index 1347532a0..a922f92f3 100644
--- a/README.md
+++ b/README.md
@@ -89,6 +89,42 @@ You can set up Zero in two ways:
Visit [http://localhost:3000](http://localhost:3000)
+
+Devcontainer Setup
+
+#### Quick Start guide
+
+1. **Clone and Install**
+
+ ```bash
+ # Clone the repository
+ git clone https://github.com/Mail-0/Zero.git
+ cd Zero
+ ```
+
+ Then open the code in devcontainer and install the dependencies:
+
+ ```
+ pnpm install
+
+ # Start the database locally
+ pnpm docker:db:up
+ ```
+
+2. **Set Up Environment**
+
+ - Run `pnpm nizzy env` to setup your environment variables
+ - Run `pnpm nizzy sync` to sync your environment variables and types
+ - Start the database with the provided docker compose setup: `pnpm docker:db:up`
+ - Initialize the database: `pnpm db:push`
+
+3. **Start The App**
+ ```bash
+ pnpm dev
+ ```
+ Visit [http://localhost:3000](http://localhost:3000)
+
+
### Environment Setup
1. **Better Auth Setup**
diff --git a/apps/mail/components/mail/mail-display.tsx b/apps/mail/components/mail/mail-display.tsx
index a77119941..039f7ea03 100644
--- a/apps/mail/components/mail/mail-display.tsx
+++ b/apps/mail/components/mail/mail-display.tsx
@@ -16,7 +16,7 @@ import {
import { memo, useEffect, useMemo, useState, useRef, useCallback } from 'react';
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
-import { Briefcase, Star, StickyNote, Users, Lock } from 'lucide-react';
+import { Briefcase, Star, StickyNote, Users, Lock, Download, Printer } from 'lucide-react';
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
import { useActiveConnection } from '@/hooks/use-connections';
import { handleUnsubscribe } from '@/lib/email-utils.client';
@@ -310,9 +310,9 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
() =>
emailData.listUnsubscribe
? getListUnsubscribeAction({
- listUnsubscribe: emailData.listUnsubscribe,
- listUnsubscribePost: emailData.listUnsubscribePost,
- })
+ listUnsubscribe: emailData.listUnsubscribe,
+ listUnsubscribePost: emailData.listUnsubscribePost,
+ })
: undefined,
[emailData.listUnsubscribe, emailData.listUnsubscribePost],
);
@@ -370,6 +370,347 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
}
}, [isCollapsed, preventCollapse, openDetailsPopover]);
+ // email printing
+ const printMail = () => {
+ try {
+ // Create a hidden iframe for printing
+ const printFrame = document.createElement('iframe');
+ printFrame.style.position = 'absolute';
+ printFrame.style.top = '-9999px';
+ printFrame.style.left = '-9999px';
+ printFrame.style.width = '0px';
+ printFrame.style.height = '0px';
+ printFrame.style.border = 'none';
+
+ document.body.appendChild(printFrame);
+
+ // Generate clean, simple HTML content for printing
+ const printContent = `
+
+
+
+
+ Print Email - ${emailData.subject || 'No Subject'}
+
+
+
+
+
+
+
+
+
+
+
+
+ ${emailData.decodedBody || '
No email content available
'}
+
+
+
+
+ ${emailData.attachments && emailData.attachments.length > 0 ? `
+
+
Attachments (${emailData.attachments.length})
+ ${emailData.attachments.map((attachment, index) => `
+
+ ${attachment.filename}
+ ${formatFileSize(attachment.size) ? ` - ${formatFileSize(attachment.size)}` : ''}
+
+ `).join('')}
+
+ ` : ''}
+
+
+
+ `;
+
+ // Write content to the iframe
+ const iframeDoc = printFrame.contentDocument || printFrame.contentWindow.document;
+ iframeDoc.open();
+ iframeDoc.write(printContent);
+ iframeDoc.close();
+
+ // Wait for content to load, then print
+ printFrame.onload = function () {
+ setTimeout(() => {
+ try {
+ // Focus the iframe and print
+ printFrame.contentWindow.focus();
+ printFrame.contentWindow.print();
+
+ // Clean up - remove the iframe after a delay
+ setTimeout(() => {
+ if (printFrame && printFrame.parentNode) {
+ document.body.removeChild(printFrame);
+ }
+ }, 1000);
+ } catch (error) {
+ console.error('Error during print:', error);
+ // Clean up on error
+ if (printFrame && printFrame.parentNode) {
+ document.body.removeChild(printFrame);
+ }
+ }
+ }, 500);
+ };
+
+ } catch (error) {
+ console.error('Error printing email:', error);
+ alert('Failed to print email. Please try again.');
+ }
+ };
+
const renderPerson = useCallback(
(person: Sender) => (
@@ -621,9 +962,29 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
-
+
+
+
+
+ {/* print button */}
+
+
@@ -744,9 +1105,11 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
>
+ {/* mail main body */}
{emailData?.decodedBody ? (
) : null}
+ {/* mail attachments */}
{emailData?.attachments && emailData?.attachments.length > 0 ? (
{emailData?.attachments.map((attachment, index) => (
@@ -860,6 +1223,18 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
f
+ {/* */}