diff --git a/apps/x/apps/renderer/package.json b/apps/x/apps/renderer/package.json
index 5d882f88..9e31cfd3 100644
--- a/apps/x/apps/renderer/package.json
+++ b/apps/x/apps/renderer/package.json
@@ -43,6 +43,7 @@
"nanoid": "^5.1.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
+ "sonner": "^2.0.7",
"streamdown": "^1.6.10",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx
index b5569277..fe0089e7 100644
--- a/apps/x/apps/renderer/src/App.tsx
+++ b/apps/x/apps/renderer/src/App.tsx
@@ -46,6 +46,7 @@ import {
} from "@/components/ui/sidebar"
import { TooltipProvider } from "@/components/ui/tooltip"
import { Separator } from "@/components/ui/separator"
+import { Toaster } from "@/components/ui/sonner"
import { stripKnowledgePrefix, toKnowledgePath, wikiLabel } from '@/lib/wiki-links'
type DirEntry = z.infer
@@ -1647,6 +1648,7 @@ function App() {
)}
+
)
}
diff --git a/apps/x/apps/renderer/src/components/chat-sidebar.tsx b/apps/x/apps/renderer/src/components/chat-sidebar.tsx
index bbb2151f..5c5f7713 100644
--- a/apps/x/apps/renderer/src/components/chat-sidebar.tsx
+++ b/apps/x/apps/renderer/src/components/chat-sidebar.tsx
@@ -145,7 +145,6 @@ export function ChatSidebar({
recentFiles = [],
visibleFiles = [],
selectedPath,
- pendingPermissionRequests = new Map(),
pendingAskHumanRequests = new Map(),
allPermissionRequests = new Map(),
permissionResponses = new Map(),
diff --git a/apps/x/apps/renderer/src/components/connectors-popover.tsx b/apps/x/apps/renderer/src/components/connectors-popover.tsx
index 145997dd..fc814302 100644
--- a/apps/x/apps/renderer/src/components/connectors-popover.tsx
+++ b/apps/x/apps/renderer/src/components/connectors-popover.tsx
@@ -2,7 +2,7 @@
import * as React from "react"
import { useState, useEffect, useCallback } from "react"
-import { Database, Loader2, Plug } from "lucide-react"
+import { Calendar, Loader2, Mic, Mail } from "lucide-react"
import {
Popover,
@@ -18,7 +18,7 @@ import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Switch } from "@/components/ui/switch"
import { Separator } from "@/components/ui/separator"
-import { toast } from "@/lib/toast"
+import { toast } from "sonner"
interface ProviderState {
isConnected: boolean
@@ -78,10 +78,10 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
setGranolaLoading(true)
await window.ipc.invoke('granola:setConfig', { enabled })
setGranolaEnabled(enabled)
- toast(enabled ? 'Granola sync enabled' : 'Granola sync disabled', 'success')
+ toast.success(enabled ? 'Granola sync enabled' : 'Granola sync disabled')
} catch (error) {
console.error('Failed to update Granola config:', error)
- toast('Failed to update Granola sync settings', 'error')
+ toast.error('Failed to update Granola sync settings')
} finally {
setGranolaLoading(false)
}
@@ -142,14 +142,23 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}))
if (success) {
- toast(`Successfully connected to ${provider}`, 'success')
+ const displayName = provider === 'fireflies-ai' ? 'Fireflies' : provider.charAt(0).toUpperCase() + provider.slice(1)
+ // Show detailed message for Google and Fireflies (includes sync info)
+ if (provider === 'google' || provider === 'fireflies-ai') {
+ toast.success(`Connected to ${displayName}`, {
+ description: 'Syncing your data in the background. This may take a few minutes before changes appear.',
+ duration: 8000,
+ })
+ } else {
+ toast.success(`Connected to ${displayName}`)
+ }
// Refresh status to ensure consistency
refreshAllStatuses()
} else {
- toast(error || `Failed to connect to ${provider}`, 'error')
+ toast.error(error || `Failed to connect to ${provider}`)
}
})
-
+
return cleanup
}, [refreshAllStatuses])
@@ -168,7 +177,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
// Event listener will handle the actual completion
} else {
// Immediate failure (e.g., couldn't start flow)
- toast(result.error || `Failed to connect to ${provider}`, 'error')
+ toast.error(result.error || `Failed to connect to ${provider}`)
setProviderStates(prev => ({
...prev,
[provider]: { ...prev[provider], isConnecting: false }
@@ -176,7 +185,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
} catch (error) {
console.error('Failed to connect:', error)
- toast(`Failed to connect to ${provider}`, 'error')
+ toast.error(`Failed to connect to ${provider}`)
setProviderStates(prev => ({
...prev,
[provider]: { ...prev[provider], isConnecting: false }
@@ -195,7 +204,8 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
const result = await window.ipc.invoke('oauth:disconnect', { provider })
if (result.success) {
- toast(`Disconnected from ${provider}`, 'success')
+ const displayName = provider === 'fireflies-ai' ? 'Fireflies' : provider.charAt(0).toUpperCase() + provider.slice(1)
+ toast.success(`Disconnected from ${displayName}`)
setProviderStates(prev => ({
...prev,
[provider]: {
@@ -205,7 +215,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
}))
} else {
- toast(`Failed to disconnect from ${provider}`, 'error')
+ toast.error(`Failed to disconnect from ${provider}`)
setProviderStates(prev => ({
...prev,
[provider]: { ...prev[provider], isLoading: false }
@@ -213,7 +223,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
} catch (error) {
console.error('Failed to disconnect:', error)
- toast(`Failed to disconnect from ${provider}`, 'error')
+ toast.error(`Failed to disconnect from ${provider}`)
setProviderStates(prev => ({
...prev,
[provider]: { ...prev[provider], isLoading: false }
@@ -221,6 +231,64 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
}, [])
+ // Helper to render an OAuth provider row
+ const renderOAuthProvider = (provider: string, displayName: string, icon: React.ReactNode, description: string) => {
+ const state = providerStates[provider] || {
+ isConnected: false,
+ isLoading: true,
+ isConnecting: false,
+ }
+
+ return (
+
+
+
+ {icon}
+
+
+ {displayName}
+ {state.isLoading ? (
+ Checking...
+ ) : (
+ {description}
+ )}
+
+
+
+ {state.isLoading ? (
+
+ ) : state.isConnected ? (
+
+ ) : (
+
+ )}
+
+
+ )
+ }
+
return (
{tooltip ? (
@@ -252,123 +320,56 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
- {/* Data Sources Section */}
-
- Data Sources
-
-
- {/* Granola */}
-
-
-
-
-
-
- Granola
-
- Sync meeting notes
-
-
-
-
- {granolaLoading && (
-
- )}
-
-
-
-
-
-
- {/* OAuth Connectors Section */}
-
- Accounts
-
-
{providersLoading ? (
- ) : providers.length === 0 ? (
-
- No account connectors available
-
) : (
-
- {providers.map((provider) => {
- const state = providerStates[provider] || {
- isConnected: false,
- isLoading: true,
- isConnecting: false,
- }
- const displayName = provider.charAt(0).toUpperCase() + provider.slice(1)
-
- return (
-
-
-
-
-
- {displayName}
-
- {state.isLoading ? (
-
- Checking...
-
- ) : (
-
- {state.isConnected ? "Connected" : "Not Connected"}
-
- )}
-
-
-
- {state.isConnected ? (
-
- ) : (
-
- )}
-
+ <>
+ {/* Email & Calendar Section - Google */}
+ {providers.includes('google') && (
+ <>
+
+ Email & Calendar
- )
- })}
-
+ {renderOAuthProvider('google', 'Google',
, 'Sync emails and calendar')}
+
+ >
+ )}
+
+ {/* Meeting Notes Section - Granola & Fireflies */}
+
+ Meeting Notes
+
+
+ {/* Granola */}
+
+
+
+
+
+
+ Granola
+
+ Local meeting notes
+
+
+
+
+ {granolaLoading && (
+
+ )}
+
+
+
+
+ {/* Fireflies */}
+ {providers.includes('fireflies-ai') && renderOAuthProvider('fireflies-ai', 'Fireflies',
, 'AI meeting transcripts')}
+ >
)}
diff --git a/apps/x/apps/renderer/src/components/ui/sonner.tsx b/apps/x/apps/renderer/src/components/ui/sonner.tsx
new file mode 100644
index 00000000..490ba36c
--- /dev/null
+++ b/apps/x/apps/renderer/src/components/ui/sonner.tsx
@@ -0,0 +1,34 @@
+import {
+ CircleCheckIcon,
+ InfoIcon,
+ Loader2Icon,
+ OctagonXIcon,
+ TriangleAlertIcon,
+} from "lucide-react"
+import { Toaster as Sonner, type ToasterProps } from "sonner"
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ return (
+
,
+ info:
,
+ warning:
,
+ error:
,
+ loading:
,
+ }}
+ style={
+ {
+ "--normal-bg": "var(--popover)",
+ "--normal-text": "var(--popover-foreground)",
+ "--normal-border": "var(--border)",
+ "--border-radius": "var(--radius)",
+ } as React.CSSProperties
+ }
+ {...props}
+ />
+ )
+}
+
+export { Toaster }
diff --git a/apps/x/pnpm-lock.yaml b/apps/x/pnpm-lock.yaml
index 490581f0..35be13a7 100644
--- a/apps/x/pnpm-lock.yaml
+++ b/apps/x/pnpm-lock.yaml
@@ -202,6 +202,9 @@ importers:
react-dom:
specifier: ^19.2.0
version: 19.2.3(react@19.2.3)
+ sonner:
+ specifier: ^2.0.7
+ version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
streamdown:
specifier: ^1.6.10
version: 1.6.10(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3)
@@ -5773,6 +5776,12 @@ packages:
resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
+ sonner@2.0.7:
+ resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -13176,6 +13185,11 @@ snapshots:
ip-address: 10.1.0
smart-buffer: 4.2.0
+ sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+
source-map-js@1.2.1: {}
source-map-support@0.5.21: