Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
0673658
basis for drive support
elie222 Dec 19, 2025
e7c854c
refactor oauth for calendar and add drive support
elie222 Dec 19, 2025
82941fd
add connect drive ui
elie222 Dec 19, 2025
2bffca1
remove frontend logs
elie222 Dec 19, 2025
e5fe3b3
capture exceptions
elie222 Dec 19, 2025
84d5df5
update schema and add prompt
elie222 Dec 20, 2025
bad9af7
filebot utils
elie222 Dec 20, 2025
d1afc93
Merge branch 'main' into feat/folders
elie222 Dec 20, 2025
c9589b4
doc extraction util
elie222 Dec 20, 2025
60cb263
Merge branch 'main' into feat/folders
elie222 Dec 21, 2025
9fe0ac1
address comments
elie222 Dec 21, 2025
8f88380
implemented more of drive support
elie222 Dec 21, 2025
2c6bec4
complete flow
elie222 Dec 21, 2025
e93d311
use helper
elie222 Dec 21, 2025
328e402
Merge branch 'main' into feat/folders
elie222 Dec 21, 2025
acc0873
delete file
elie222 Dec 21, 2025
fe57a74
fix
elie222 Dec 21, 2025
be261e7
Merge branch 'main' into feat/folders
elie222 Dec 21, 2025
9d2b48e
migration and sidebar link
elie222 Dec 21, 2025
9a30c79
adjust styling
elie222 Dec 21, 2025
5e7938f
onboarding for auto file
elie222 Dec 21, 2025
3d94b4b
onboarding and wip styling
elie222 Dec 21, 2025
936e838
update readme
elie222 Dec 21, 2025
b8e9a2d
move toggle to top right
elie222 Dec 21, 2025
5e3b118
add kibo ui treeview
elie222 Dec 21, 2025
5bbeb61
no folders found
elie222 Dec 21, 2025
c19a330
update turbo
elie222 Dec 21, 2025
8a37297
use small card
elie222 Dec 21, 2025
55e1995
error if cant connect to drive
elie222 Dec 21, 2025
ccf0c11
refresh drive func
elie222 Dec 21, 2025
7249bc2
load sub folders
elie222 Dec 21, 2025
313db1f
badge
elie222 Dec 22, 2025
70e9000
adjust ui to select folders and prompt
elie222 Dec 22, 2025
b845a4d
refactor
elie222 Dec 22, 2025
5dde067
colocate action
elie222 Dec 22, 2025
b241b4e
force onboarding view
elie222 Dec 22, 2025
f64e6a7
setup screen
elie222 Dec 22, 2025
3e7426b
show preview in setup
elie222 Dec 22, 2025
84048f0
filing within setup
elie222 Dec 22, 2025
89d2668
fix comments
elie222 Dec 22, 2025
68f9404
add tests
elie222 Dec 23, 2025
44f69f1
fix types
elie222 Dec 23, 2025
c117b33
fix failing test
elie222 Dec 23, 2025
501a788
fix build
elie222 Dec 23, 2025
23f74b9
remove unnecssary token
elie222 Dec 23, 2025
ebd1d5b
Merge branch 'main' into feat/folders
elie222 Dec 24, 2025
747e3c5
fix tests
elie222 Dec 24, 2025
28ca749
fixes
elie222 Dec 24, 2025
dc11999
fixes
elie222 Dec 25, 2025
c88f031
reuse existing styles
elie222 Dec 25, 2025
43249e2
Merge branch 'main' into feat/folders
elie222 Dec 26, 2025
95efe1b
drive adjustments. reusable mutedtext
elie222 Dec 27, 2025
682f050
optimise query
elie222 Dec 27, 2025
5be142b
rename auto file to smart filing
elie222 Dec 27, 2025
a24b1fe
rename var
elie222 Dec 27, 2025
37b7c15
delete unused file
elie222 Dec 27, 2025
2b3362f
simplify code
elie222 Dec 27, 2025
9b4b69e
encrypt drive tokens
elie222 Dec 27, 2025
39ef222
clean up
elie222 Dec 27, 2025
5c0b2de
create folder if none exist
elie222 Dec 27, 2025
c4439d1
fix fetching emails with attachments
elie222 Dec 27, 2025
9407581
improved preview mode
elie222 Dec 27, 2025
e098f89
implement check
elie222 Dec 27, 2025
106c42b
show correct folder selector as tree
elie222 Dec 27, 2025
5146f18
faster select folder. when we create folders, store as filing folder
elie222 Dec 27, 2025
0dd0f2d
dont create new folders unless necessary
elie222 Dec 27, 2025
6d33854
adjutst filing prompt
elie222 Dec 28, 2025
fbf5ecf
tooltip for skipped
elie222 Dec 28, 2025
630ab41
reusable filing activity components
elie222 Dec 28, 2025
8559581
paginate folders
elie222 Dec 28, 2025
db6ddf9
process attachments in after()
elie222 Dec 28, 2025
b295b07
only fetch attachment when needed
elie222 Dec 28, 2025
36828fb
adjust filebot suffix
elie222 Dec 28, 2025
158b77e
ts check
elie222 Dec 28, 2025
6a927fe
remove comments
elie222 Dec 28, 2025
247ff47
move helpers into single
elie222 Dec 28, 2025
9f52f0b
clean up
elie222 Dec 28, 2025
a1d4aa0
refactor addFilingFolderAction to consolidate folder data into a sing…
elie222 Dec 28, 2025
e984a09
handle error
elie222 Dec 28, 2025
9c4c121
clean up set up code
elie222 Dec 28, 2025
c955629
fixes
elie222 Dec 28, 2025
e89be0b
Merge branch 'main' into feat/folders
elie222 Jan 1, 2026
7270769
Merge branch 'main' into feat/folders
elie222 Jan 4, 2026
84f071a
fix(gmail): improve filter error logging and add limit check
elie222 Jan 4, 2026
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ To help you spend less time in your inbox, so you can focus on what matters.
- **Bulk Unsubscriber:** One-click unsubscribe and archive emails you never read.
- **Cold Email Blocker:** Auto‑block cold emails.
- **Email Analytics:** Track your activity and trends over time.
- **Meeting Briefs (Beta):** Get personalized briefings before every meeting, pulling context from your email and calendar.
- **Smart Filing (Early Access):** Automatically save email attachments to Google Drive or OneDrive.


Learn more in our [docs](https://docs.getinboxzero.com).

Expand Down Expand Up @@ -156,6 +159,7 @@ Create [new credentials](https://console.cloud.google.com/apis/credentials):
- `http://localhost:3000/api/auth/callback/google`
- `http://localhost:3000/api/google/linking/callback`
- `http://localhost:3000/api/google/calendar/callback` (only required for calendar integration)
- `http://localhost:3000/api/google/drive/callback` (only required for Google Drive integration)
6. Click `Create`.
7. A popup will show up with the new credentials, including the Client ID and secret.
3. Update .env file:
Expand All @@ -174,6 +178,7 @@ Create [new credentials](https://console.cloud.google.com/apis/credentials):
https://www.googleapis.com/auth/gmail.settings.basic
https://www.googleapis.com/auth/contacts
https://www.googleapis.com/auth/calendar (only required for calendar integration)
https://www.googleapis.com/auth/drive.file (only required for Google Drive integration)
```

4. Click `Update`
Expand All @@ -187,6 +192,7 @@ Create [new credentials](https://console.cloud.google.com/apis/credentials):
6. Enable required APIs in [Google Cloud Console](https://console.cloud.google.com/apis/library):
- [Google People API](https://console.cloud.google.com/marketplace/product/google/people.googleapis.com) (required)
- [Google Calendar API](https://console.cloud.google.com/marketplace/product/google/calendar-json.googleapis.com) (only required for calendar integration)
- [Google Drive API](https://console.cloud.google.com/marketplace/product/google/drive.googleapis.com) (only required for Google Drive integration)

### Google PubSub Setup

Expand Down Expand Up @@ -233,6 +239,7 @@ Go to [Microsoft Azure Portal](https://portal.azure.com/) and create a new Azure
6. Add the following Redirect URIs (replace `localhost:3000` with your domain in production):
- `http://localhost:3000/api/outlook/linking/callback`
- `http://localhost:3000/api/outlook/calendar/callback` (only required for calendar integration)
- `http://localhost:3000/api/outlook/drive/callback` (only required for OneDrive integration)

4. Get your credentials from the `Overview` tab:

Expand Down Expand Up @@ -262,6 +269,7 @@ Go to [Microsoft Azure Portal](https://portal.azure.com/) and create a new Azure
- MailboxSettings.ReadWrite
- Calendars.Read (only required for calendar integration)
- Calendars.ReadWrite (only required for calendar integration)
- Files.ReadWrite (only required for OneDrive integration)

6. Click "Add permissions"
7. Click "Grant admin consent" if you're an admin
Expand Down
5 changes: 5 additions & 0 deletions apps/web/app/(app)/(redirects)/drive/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirectToEmailAccountPath } from "@/utils/account";

export default async function DrivePage() {
await redirectToEmailAccountPath("/drive");
}
9 changes: 5 additions & 4 deletions apps/web/app/(app)/[emailAccountId]/assistant/ActionSteps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { WebhookDocumentationLink } from "@/components/WebhookDocumentation";
import { LabelCombobox } from "@/components/LabelCombobox";
import { RuleStep } from "@/app/(app)/[emailAccountId]/assistant/RuleStep";
import { Card } from "@/components/ui/card";
import { MutedText } from "@/components/Typography";

export function ActionSteps({
actionFields,
Expand Down Expand Up @@ -538,15 +539,15 @@ function ActionCard({
const rightContent = (
<>
{isNotifySender ? (
<div className="px-1 h-full flex items-center text-sm text-muted-foreground">
<MutedText className="px-1 h-full flex items-center">
Sends an automated notification from Inbox Zero informing the sender
their email was filtered as cold outreach.
</div>
</MutedText>
) : isDraftEmailWithoutManualContent ? (
<div className="px-1 h-full flex items-center text-sm text-muted-foreground">
<MutedText className="px-1 h-full flex items-center">
Our AI generates a draft reply from your email history and knowledge
base.
</div>
</MutedText>
) : isEmailAction || actionType === ActionType.CALL_WEBHOOK ? (
<Card className="p-4 space-y-4">
{fieldsContent}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { ResultsDisplay } from "@/app/(app)/[emailAccountId]/assistant/ResultDis
import { useAccount } from "@/providers/EmailAccountProvider";
import { FixWithChat } from "@/app/(app)/[emailAccountId]/assistant/FixWithChat";
import { useChat } from "@/providers/ChatProvider";
import { MutedText } from "@/components/Typography";

type Message = MessagesResponse["messages"][number];

Expand Down Expand Up @@ -287,9 +288,7 @@ export function ProcessRulesContent({ testMode }: { testMode: boolean }) {

<LoadingContent loading={isLoading} error={error}>
{messages.length === 0 ? (
<div className="p-4 text-center text-sm text-muted-foreground">
No emails found
</div>
<MutedText className="p-4 text-center">No emails found</MutedText>
) : (
<Card>
<Table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ExecutedRuleStatus, LogicalOperator } from "@/generated/prisma/enums";
import type { ActionType } from "@/generated/prisma/enums";
import type { Rule } from "@/generated/prisma/client";
import { Button } from "@/components/ui/button";
import { MessageText } from "@/components/Typography";
import { MessageText, MutedText } from "@/components/Typography";
import { EyeIcon } from "lucide-react";
import { useRuleDialog } from "@/app/(app)/[emailAccountId]/assistant/RuleDialog";
import type { RunRulesResult } from "@/utils/ai/choose-rule/run-rules";
Expand Down Expand Up @@ -211,7 +211,7 @@ function Actions({
{getActionDisplay(action, provider, labels)}
</Badge>
{fields.length > 0 && (
<div className="ml-1 text-sm text-muted-foreground space-y-0.5">
<MutedText className="ml-1 space-y-0.5">
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Jan 4, 2026

Choose a reason for hiding this comment

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

P1: Invalid HTML nesting: MutedText renders as a <p> element, but it wraps <div> children. HTML does not allow <div> inside <p> - browsers will auto-close the <p> before the first <div>, breaking the layout.

Consider keeping the original <div> or changing the inner elements to <span>.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx, line 214:

<comment>Invalid HTML nesting: `MutedText` renders as a `&lt;p&gt;` element, but it wraps `&lt;div&gt;` children. HTML does not allow `&lt;div&gt;` inside `&lt;p&gt;` - browsers will auto-close the `&lt;p&gt;` before the first `&lt;div&gt;`, breaking the layout.

Consider keeping the original `&lt;div&gt;` or changing the inner elements to `&lt;span&gt;`.</comment>

<file context>
@@ -211,7 +211,7 @@ function Actions({
             &lt;/Badge&gt;
             {fields.length &gt; 0 &amp;&amp; (
-              &lt;div className=&quot;ml-1 text-sm text-muted-foreground space-y-0.5&quot;&gt;
+              &lt;MutedText className=&quot;ml-1 space-y-0.5&quot;&gt;
                 {fields.map((field) =&gt; (
                   &lt;div
</file context>
Fix with Cubic

{fields.map((field) => (
<div
key={field.key}
Expand All @@ -221,7 +221,7 @@ function Actions({
{field.value}
</div>
))}
</div>
</MutedText>
)}
</div>
);
Expand Down Expand Up @@ -258,7 +258,7 @@ function PrettyConditions({
<div className="flex flex-wrap items-center gap-1.5">
{conditions.map((condition, index) => (
<div key={index} className="flex items-center gap-1.5">
<span className="text-sm text-muted-foreground">{condition}</span>
<MutedText>{condition}</MutedText>
{index < conditions.length - 1 && (
<Badge color="purple" className="text-xs">
{operator}
Expand Down
5 changes: 3 additions & 2 deletions apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
STEP_KEYS,
getStepNumber,
} from "@/app/(app)/[emailAccountId]/onboarding/steps";
import { MutedText } from "@/components/Typography";

export function Rules({
showAddRuleButton = true,
Expand Down Expand Up @@ -246,9 +247,9 @@ export function Rules({
if (isConversationStatus) {
return (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">
<MutedText>
{systemRuleDesc?.condition || ""}
</span>
</MutedText>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="size-3.5 text-green-600 dark:text-green-500 flex-shrink-0 cursor-help" />
Expand Down
5 changes: 3 additions & 2 deletions apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Rules } from "@/app/(app)/[emailAccountId]/assistant/Rules";
import { AddRuleDialog } from "@/app/(app)/[emailAccountId]/assistant/AddRuleDialog";
import { MutedText } from "@/components/Typography";

export function RulesTab() {
return (
<div>
<div className="flex items-center mb-2 justify-between">
<p className="text-sm text-muted-foreground">
<MutedText>
Your assistant automatically organizes incoming emails using these
rules.
</p>
</MutedText>

<AddRuleDialog />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
TableHeader,
TableHead,
} from "@/components/ui/table";
import { MessageText } from "@/components/Typography";
import { MessageText, MutedText } from "@/components/Typography";
import {
addGroupItemAction,
deleteGroupItemAction,
Expand Down Expand Up @@ -309,9 +309,9 @@ function GroupItemList({
</TableCell>
<TableCell className="flex items-center justify-end gap-4 py-2 text-right">
<Tooltip content="Date added">
<span className="text-sm text-muted-foreground">
<MutedText>
{formatShortDate(new Date(item.createdAt))}
</span>
</MutedText>
</Tooltip>

<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import Image from "next/image";
import {
Card,
CardContent,
Expand All @@ -19,7 +20,6 @@ import { useAccount } from "@/providers/EmailAccountProvider";
import { useCalendars } from "@/hooks/useCalendars";
import { useState } from "react";
import type { GetCalendarsResponse } from "@/app/api/user/calendars/route";
import Image from "next/image";
import { TypographyP } from "@/components/Typography";
import {
Collapsible,
Expand Down
15 changes: 5 additions & 10 deletions apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import Image from "next/image";
import { Button } from "@/components/ui/button";
import { useAccount } from "@/providers/EmailAccountProvider";
import { toastError } from "@/components/Toast";
import { captureException } from "@/utils/error";
import type { GetCalendarAuthUrlResponse } from "@/app/api/google/calendar/auth-url/route";
import { fetchWithAccount } from "@/utils/fetch";
import { createScopedLogger } from "@/utils/logger";
import { CALENDAR_ONBOARDING_RETURN_COOKIE } from "@/utils/calendar/constants";

export function ConnectCalendar({
Expand All @@ -18,7 +18,6 @@ export function ConnectCalendar({
const { emailAccountId } = useAccount();
const [isConnectingGoogle, setIsConnectingGoogle] = useState(false);
const [isConnectingMicrosoft, setIsConnectingMicrosoft] = useState(false);
const logger = createScopedLogger("calendar-connection");

const setOnboardingReturnCookie = () => {
if (onboardingReturnPath) {
Expand All @@ -43,10 +42,8 @@ export function ConnectCalendar({
setOnboardingReturnCookie();
window.location.href = data.url;
} catch (error) {
logger.error("Error initiating Google calendar connection", {
error,
emailAccountId,
provider: "google",
captureException(error, {
extra: { context: "Google Calendar OAuth initiation" },
});
toastError({
title: "Error initiating Google calendar connection",
Expand All @@ -73,10 +70,8 @@ export function ConnectCalendar({
setOnboardingReturnCookie();
window.location.href = data.url;
} catch (error) {
logger.error("Error initiating Microsoft calendar connection", {
error,
emailAccountId,
provider: "microsoft",
captureException(error, {
extra: { context: "Microsoft Calendar OAuth initiation" },
});
toastError({
title: "Error initiating Microsoft calendar connection",
Expand Down
9 changes: 3 additions & 6 deletions apps/web/app/(app)/[emailAccountId]/clean/CleanHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { LoadingContent } from "@/components/LoadingContent";
import { formatDateSimple } from "@/utils/date";
import { useAccount } from "@/providers/EmailAccountProvider";
import { prefixPath } from "@/utils/path";
import { MutedText } from "@/components/Typography";

export function CleanHistory() {
const { emailAccountId } = useAccount();
Expand All @@ -29,17 +30,13 @@ export function CleanHistory() {
{formatDateSimple(new Date(job.createdAt))}
</h4>
</div>
<p className="text-sm text-muted-foreground">
{job._count.threads} emails processed
</p>
<MutedText>{job._count.threads} emails processed</MutedText>
</div>
</Link>
))}
</div>
) : (
<div className="p-4 text-center text-sm text-muted-foreground">
No history yet
</div>
<MutedText className="p-4 text-center">No history yet</MutedText>
)}
</LoadingContent>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Link from "next/link";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { TypographyH3 } from "@/components/Typography";
import { MutedText, TypographyH3 } from "@/components/Typography";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/Badge";
import { cleanInboxAction } from "@/utils/actions/clean";
Expand Down Expand Up @@ -122,7 +122,7 @@ export function ConfirmationStep({
</div>

{showFooter && (
<div className="mt-6 flex items-center justify-center space-x-6 text-sm text-muted-foreground">
<MutedText className="mt-6 flex items-center justify-center space-x-6">
<FooterLink
icon={HistoryIcon}
text="History"
Expand All @@ -133,7 +133,7 @@ export function ConfirmationStep({
text="Edit settings"
href={prefixPath(emailAccountId, "/clean/onboarding")}
/>
</div>
</MutedText>
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import prisma from "@/utils/prisma";
import { PageHeading } from "@/components/Typography";
import { MutedText, PageHeading } from "@/components/Typography";
import {
Card,
CardContent,
Expand Down Expand Up @@ -80,11 +80,11 @@ export default async function RuleHistoryPage(props: {
{triggerTypeLabels[history.triggerType] ||
history.triggerType}
</Badge>
<span className="text-sm text-muted-foreground">
<MutedText>
{formatDistanceToNow(history.createdAt, {
addSuffix: true,
})}
</span>
</MutedText>
</div>
</div>
{history.promptText && (
Expand Down
Loading
Loading