@realtimex/email-automator 2.1.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/api/server.ts +0 -6
  2. package/api/src/config/index.ts +3 -0
  3. package/bin/email-automator-setup.js +2 -3
  4. package/bin/email-automator.js +23 -7
  5. package/package.json +1 -2
  6. package/src/App.tsx +0 -622
  7. package/src/components/AccountSettings.tsx +0 -310
  8. package/src/components/AccountSettingsPage.tsx +0 -390
  9. package/src/components/Configuration.tsx +0 -1345
  10. package/src/components/Dashboard.tsx +0 -940
  11. package/src/components/ErrorBoundary.tsx +0 -71
  12. package/src/components/LiveTerminal.tsx +0 -308
  13. package/src/components/LoadingSpinner.tsx +0 -39
  14. package/src/components/Login.tsx +0 -371
  15. package/src/components/Logo.tsx +0 -57
  16. package/src/components/SetupWizard.tsx +0 -388
  17. package/src/components/Toast.tsx +0 -109
  18. package/src/components/migration/MigrationBanner.tsx +0 -97
  19. package/src/components/migration/MigrationModal.tsx +0 -458
  20. package/src/components/migration/MigrationPulseIndicator.tsx +0 -38
  21. package/src/components/mode-toggle.tsx +0 -24
  22. package/src/components/theme-provider.tsx +0 -72
  23. package/src/components/ui/alert.tsx +0 -66
  24. package/src/components/ui/button.tsx +0 -57
  25. package/src/components/ui/card.tsx +0 -75
  26. package/src/components/ui/dialog.tsx +0 -133
  27. package/src/components/ui/input.tsx +0 -22
  28. package/src/components/ui/label.tsx +0 -24
  29. package/src/components/ui/otp-input.tsx +0 -184
  30. package/src/context/AppContext.tsx +0 -422
  31. package/src/context/MigrationContext.tsx +0 -53
  32. package/src/context/TerminalContext.tsx +0 -31
  33. package/src/core/actions.ts +0 -76
  34. package/src/core/auth.ts +0 -108
  35. package/src/core/intelligence.ts +0 -76
  36. package/src/core/processor.ts +0 -112
  37. package/src/hooks/useRealtimeEmails.ts +0 -111
  38. package/src/index.css +0 -140
  39. package/src/lib/api-config.ts +0 -42
  40. package/src/lib/api-old.ts +0 -228
  41. package/src/lib/api.ts +0 -421
  42. package/src/lib/migration-check.ts +0 -264
  43. package/src/lib/sounds.ts +0 -120
  44. package/src/lib/supabase-config.ts +0 -117
  45. package/src/lib/supabase.ts +0 -28
  46. package/src/lib/types.ts +0 -166
  47. package/src/lib/utils.ts +0 -6
  48. package/src/main.tsx +0 -10
@@ -1,71 +0,0 @@
1
- import { Component, ReactNode } from 'react';
2
- import { AlertTriangle, RefreshCw } from 'lucide-react';
3
- import { Button } from './ui/button';
4
- import { Card } from './ui/card';
5
-
6
- interface Props {
7
- children: ReactNode;
8
- fallback?: ReactNode;
9
- }
10
-
11
- interface State {
12
- hasError: boolean;
13
- error: Error | null;
14
- }
15
-
16
- export class ErrorBoundary extends Component<Props, State> {
17
- constructor(props: Props) {
18
- super(props);
19
- this.state = { hasError: false, error: null };
20
- }
21
-
22
- static getDerivedStateFromError(error: Error): State {
23
- return { hasError: true, error };
24
- }
25
-
26
- componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
27
- console.error('ErrorBoundary caught an error:', error, errorInfo);
28
- }
29
-
30
- handleReset = () => {
31
- this.setState({ hasError: false, error: null });
32
- };
33
-
34
- render() {
35
- if (this.state.hasError) {
36
- if (this.props.fallback) {
37
- return this.props.fallback;
38
- }
39
-
40
- return (
41
- <div className="min-h-screen flex items-center justify-center p-8 bg-background">
42
- <Card className="max-w-md w-full p-8 text-center">
43
- <div className="w-16 h-16 bg-destructive/10 text-destructive rounded-full flex items-center justify-center mx-auto mb-6">
44
- <AlertTriangle className="w-8 h-8" />
45
- </div>
46
- <h2 className="text-xl font-semibold mb-2">Something went wrong</h2>
47
- <p className="text-muted-foreground mb-6">
48
- An unexpected error occurred. Please try refreshing the page.
49
- </p>
50
- {this.state.error && (
51
- <pre className="text-xs text-left bg-secondary p-3 rounded-lg mb-6 overflow-auto max-h-32">
52
- {this.state.error.message}
53
- </pre>
54
- )}
55
- <div className="flex gap-3 justify-center">
56
- <Button onClick={this.handleReset} variant="outline">
57
- <RefreshCw className="w-4 h-4 mr-2" />
58
- Try Again
59
- </Button>
60
- <Button onClick={() => window.location.reload()}>
61
- Refresh Page
62
- </Button>
63
- </div>
64
- </Card>
65
- </div>
66
- );
67
- }
68
-
69
- return this.props.children;
70
- }
71
- }
@@ -1,308 +0,0 @@
1
- import { useEffect, useState, useRef } from 'react';
2
- import { supabase } from '../lib/supabase';
3
- import { ProcessingEvent } from '../lib/types';
4
- import {
5
- Terminal,
6
- Brain,
7
- Zap,
8
- Info,
9
- AlertTriangle,
10
- Activity,
11
- Minimize2,
12
- ChevronDown,
13
- ChevronUp,
14
- Code
15
- } from 'lucide-react';
16
- import { Card, CardHeader, CardTitle, CardContent } from './ui/card';
17
- import { Button } from './ui/button';
18
- import { cn } from '../lib/utils';
19
- import { useTerminal } from '../context/TerminalContext';
20
-
21
- export function LiveTerminal() {
22
- const [events, setEvents] = useState<ProcessingEvent[]>([]);
23
- const { isExpanded, setIsExpanded } = useTerminal();
24
- const [expandedEvents, setExpandedEvents] = useState<Record<string, boolean>>({});
25
-
26
- // Initial fetch of recent events
27
- useEffect(() => {
28
- fetchRecentEvents();
29
-
30
- // Subscribe to realtime changes
31
- const channel = supabase
32
- .channel('processing_events_feed')
33
- .on(
34
- 'postgres_changes',
35
- {
36
- event: 'INSERT',
37
- schema: 'public',
38
- table: 'processing_events',
39
- },
40
- (payload) => {
41
- const newEvent = payload.new as ProcessingEvent;
42
-
43
- // Auto-expand errors
44
- if (newEvent.event_type === 'error') {
45
- setExpandedEvents(prev => ({ ...prev, [newEvent.id]: true }));
46
- }
47
-
48
- setEvents((prev) => {
49
- // Insert at the beginning (descending order)
50
- const updated = [newEvent, ...prev];
51
- if (updated.length > 100) return updated.slice(0, 100);
52
- return updated;
53
- });
54
- }
55
- )
56
- .subscribe();
57
-
58
- return () => {
59
- supabase.removeChannel(channel);
60
- };
61
- }, []);
62
-
63
- const fetchRecentEvents = async () => {
64
- const { data } = await supabase
65
- .from('processing_events')
66
- .select('*')
67
- .order('created_at', { ascending: false })
68
- .limit(50);
69
-
70
- if (data) {
71
- setEvents(data);
72
- }
73
- };
74
-
75
- const toggleExpand = (id: string) => {
76
- setExpandedEvents(prev => ({ ...prev, [id]: !prev[id] }));
77
- };
78
-
79
- const getIcon = (type: string) => {
80
- switch (type) {
81
- case 'analysis': return <Brain className="w-3 h-3 text-purple-500" />;
82
- case 'action': return <Zap className="w-3 h-3 text-emerald-500" />;
83
- case 'error': return <AlertTriangle className="w-3 h-3 text-red-500" />;
84
- default: return <Info className="w-3 h-3 text-blue-500" />;
85
- }
86
- };
87
-
88
- const formatTime = (isoString: string) => {
89
- return new Date(isoString).toLocaleTimeString([], { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
90
- };
91
-
92
- if (!isExpanded) {
93
- return (
94
- <div className="fixed bottom-4 right-4 z-50">
95
- <Button
96
- onClick={() => setIsExpanded(true)}
97
- className="shadow-lg bg-primary text-primary-foreground hover:opacity-90 border border-border"
98
- >
99
- <Terminal className="w-4 h-4 mr-2" />
100
- Live Activity
101
- {events.length > 0 && (
102
- <span className="ml-2 flex h-2 w-2 relative">
103
- <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
104
- <span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
105
- </span>
106
- )}
107
- </Button>
108
- </div>
109
- );
110
- }
111
-
112
- return (
113
- <Card className="fixed bottom-4 right-4 z-50 w-[550px] h-[650px] flex flex-col shadow-2xl border-border bg-background/95 text-foreground backdrop-blur-md animate-in slide-in-from-bottom-10">
114
- <CardHeader className="py-3 px-4 border-b border-border flex flex-row items-center justify-between sticky top-0 bg-background/95 z-20">
115
- <div className="flex items-center gap-2">
116
- <Terminal className="w-4 h-4 text-primary" />
117
- <CardTitle className="text-sm font-mono font-bold">Agent Terminal</CardTitle>
118
- <div className="flex items-center gap-1 text-[10px] text-green-600 dark:text-green-400 bg-green-500/10 px-2 py-0.5 rounded-full border border-green-500/20 font-bold">
119
- <Activity className="w-3 h-3" />
120
- LIVE
121
- </div>
122
- </div>
123
- <div className="flex items-center gap-2">
124
- <Button
125
- variant="ghost"
126
- size="sm"
127
- className="h-7 text-[10px] font-mono hover:bg-secondary"
128
- onClick={() => setEvents([])}
129
- >
130
- Clear
131
- </Button>
132
- <Button
133
- variant="ghost"
134
- size="sm"
135
- className="h-7 w-7 p-0 hover:bg-secondary"
136
- onClick={() => setIsExpanded(false)}
137
- >
138
- <Minimize2 className="w-4 h-4" />
139
- </Button>
140
- </div>
141
- </CardHeader>
142
- <CardContent className="flex-1 overflow-y-auto p-4 font-mono text-xs space-y-6 custom-scrollbar">
143
- {events.length === 0 && (
144
- <div className="text-center text-muted-foreground py-20 italic">
145
- Waiting for agent activity...
146
- </div>
147
- )}
148
-
149
- {events.map((event, i) => (
150
- <div key={event.id} className="relative pl-8 animate-in fade-in slide-in-from-top-2 duration-300">
151
- {/* Connecting Line */}
152
- {i !== events.length - 1 && (
153
- <div className="absolute left-[13px] top-7 bottom-[-24px] w-[1px] bg-border" />
154
- )}
155
-
156
- {/* Icon Badge */}
157
- <div className={cn(
158
- "absolute left-0 top-0 w-7 h-7 rounded-full border border-border bg-card flex items-center justify-center z-10 shadow-sm",
159
- event.event_type === 'error' && "border-red-500/50 bg-red-500/5",
160
- event.event_type === 'analysis' && "border-purple-500/50 bg-purple-500/5",
161
- event.event_type === 'action' && "border-emerald-500/50 bg-emerald-500/5"
162
- )}>
163
- {getIcon(event.event_type)}
164
- </div>
165
-
166
- <div className="flex flex-col gap-1.5">
167
- <div className="flex items-center justify-between group">
168
- <div className="flex items-center gap-2">
169
- <span className={cn(
170
- "font-bold uppercase tracking-tight text-[10px]",
171
- event.event_type === 'info' && "text-muted-foreground",
172
- event.event_type === 'analysis' && "text-purple-600 dark:text-purple-400",
173
- event.event_type === 'action' && "text-emerald-600 dark:text-emerald-400",
174
- event.event_type === 'error' && "text-red-600 dark:text-red-400",
175
- )}>
176
- {event.agent_state}
177
- </span>
178
- <span className="text-[10px] text-muted-foreground/60">{formatTime(event.created_at)}</span>
179
- </div>
180
- {(event.details?.system_prompt || event.details?._raw_response || event.details?.raw_response) && (
181
- <Button
182
- variant="ghost"
183
- size="sm"
184
- className="h-5 px-1.5 text-[9px] text-muted-foreground hover:text-primary opacity-0 group-hover:opacity-100 transition-opacity"
185
- onClick={() => toggleExpand(event.id)}
186
- >
187
- {expandedEvents[event.id] ? <ChevronUp className="w-3 h-3 mr-1" /> : <ChevronDown className="w-3 h-3 mr-1" />}
188
- Details
189
- </Button>
190
- )}
191
- </div>
192
-
193
- {/* Detailed Content */}
194
- {event.event_type === 'analysis' && event.details ? (
195
- <div className="bg-purple-500/5 border border-purple-500/10 rounded-lg p-3 space-y-2">
196
- <div className="flex gap-2">
197
- <span className={cn(
198
- "px-1.5 py-0.5 rounded text-[9px] font-bold border",
199
- event.details.is_useless ? "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20" : "bg-green-500/10 text-green-600 dark:text-green-400 border-green-500/20"
200
- )}>
201
- {event.details.is_useless ? 'USELESS' : 'RELEVANT'}
202
- </span>
203
- <span className="px-1.5 py-0.5 rounded text-[9px] font-bold border bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20 uppercase">
204
- {event.details.category}
205
- </span>
206
- </div>
207
- <p className="text-foreground/90 italic leading-relaxed">
208
- "{event.details.summary}"
209
- </p>
210
- {event.details.suggested_actions && event.details.suggested_actions.length > 0 && (
211
- <div className="pt-1 flex items-center gap-2 flex-wrap">
212
- {event.details.suggested_actions.map((a: string) => (
213
- <div key={a} className="flex items-center gap-1 text-[9px] text-emerald-600 dark:text-emerald-400 font-bold bg-emerald-500/10 px-1.5 py-0.5 rounded border border-emerald-500/20 uppercase">
214
- <Zap className="w-2.5 h-2.5" />
215
- {a}
216
- </div>
217
- ))}
218
- </div>
219
- )}
220
- </div>
221
- ) : event.event_type === 'action' && event.details ? (
222
- <div className="bg-emerald-500/5 border border-emerald-500/10 rounded-lg p-3">
223
- <p className="text-emerald-600 dark:text-emerald-400 font-bold mb-1 uppercase text-[9px] tracking-widest">Execution Complete</p>
224
- <p className="text-foreground font-medium">
225
- {event.details.action === 'delete' && 'Moved to Trash'}
226
- {event.details.action === 'archive' && 'Archived Email'}
227
- {event.details.action === 'draft' && 'Drafted Reply'}
228
- {event.details.action === 'read' && 'Marked as Read'}
229
- {event.details.action === 'star' && 'Starred Email'}
230
- {!['delete', 'archive', 'draft', 'read', 'star'].includes(event.details.action) && event.details.action}
231
- </p>
232
- {event.details.reason && (
233
- <p className="text-[10px] text-muted-foreground mt-1.5 flex items-center gap-1">
234
- <Info className="w-3 h-3" />
235
- {event.details.reason}
236
- </p>
237
- )}
238
- </div>
239
- ) : event.event_type === 'error' && event.details ? (
240
- <div className="bg-red-500/5 border border-red-500/10 rounded-lg p-2.5 text-red-600 dark:text-red-400 font-medium">
241
- {event.details.error}
242
- </div>
243
- ) : (
244
- <p className="text-muted-foreground leading-relaxed">
245
- {event.details?.message || JSON.stringify(event.details)}
246
- </p>
247
- )}
248
-
249
- {/* Collapsible Technical Details */}
250
- {expandedEvents[event.id] && (
251
- <div className="mt-2 space-y-3 animate-in fade-in zoom-in-95 duration-200">
252
- {event.details?.system_prompt && (
253
- <div className="space-y-1.5">
254
- <div className="flex items-center gap-1.5 text-[9px] font-bold text-muted-foreground uppercase tracking-widest px-1">
255
- <Code className="w-3 h-3" /> System Prompt
256
- </div>
257
- <div className="bg-secondary/50 rounded-md p-3 border border-border overflow-x-auto">
258
- <pre className="whitespace-pre-wrap break-words text-[10px] leading-normal text-muted-foreground select-all">
259
- {event.details.system_prompt}
260
- </pre>
261
- </div>
262
- </div>
263
- )}
264
- {event.details?.content_preview && (
265
- <div className="space-y-1.5">
266
- <div className="flex items-center gap-1.5 text-[9px] font-bold text-muted-foreground uppercase tracking-widest px-1">
267
- <Code className="w-3 h-3" /> Input Content (Cleaned)
268
- </div>
269
- <div className="bg-secondary/50 rounded-md p-3 border border-border">
270
- <p className="whitespace-pre-wrap break-words text-[10px] text-muted-foreground">
271
- {event.details.content_preview}
272
- </p>
273
- </div>
274
- </div>
275
- )}
276
- {event.details?._raw_response && (
277
- <div className="space-y-1.5">
278
- <div className="flex items-center gap-1.5 text-[9px] font-bold text-muted-foreground uppercase tracking-widest px-1">
279
- <Code className="w-3 h-3" /> Raw LLM JSON Output
280
- </div>
281
- <div className="bg-secondary/50 rounded-md p-3 border border-border overflow-x-auto">
282
- <pre className="text-[10px] text-muted-foreground select-all">
283
- {JSON.stringify(JSON.parse(event.details._raw_response), null, 2)}
284
- </pre>
285
- </div>
286
- </div>
287
- )}
288
- {event.details?.raw_response && (
289
- <div className="space-y-1.5">
290
- <div className="flex items-center gap-1.5 text-[9px] font-bold text-muted-foreground uppercase tracking-widest px-1">
291
- <Code className="w-3 h-3" /> Raw Response (from Error)
292
- </div>
293
- <div className="bg-secondary/50 rounded-md p-3 border border-border overflow-x-auto">
294
- <pre className="text-[10px] text-muted-foreground select-all whitespace-pre-wrap">
295
- {event.details.raw_response}
296
- </pre>
297
- </div>
298
- </div>
299
- )}
300
- </div>
301
- )}
302
- </div>
303
- </div>
304
- ))}
305
- </CardContent>
306
- </Card>
307
- );
308
- }
@@ -1,39 +0,0 @@
1
- import { Loader2 } from 'lucide-react';
2
- import { cn } from '../lib/utils';
3
-
4
- interface LoadingSpinnerProps {
5
- size?: 'sm' | 'md' | 'lg';
6
- className?: string;
7
- text?: string;
8
- }
9
-
10
- export function LoadingSpinner({ size = 'md', className, text }: LoadingSpinnerProps) {
11
- const sizeClasses = {
12
- sm: 'w-4 h-4',
13
- md: 'w-8 h-8',
14
- lg: 'w-12 h-12',
15
- };
16
-
17
- return (
18
- <div className={cn('flex flex-col items-center justify-center gap-3', className)}>
19
- <Loader2 className={cn('animate-spin text-primary', sizeClasses[size])} />
20
- {text && <p className="text-sm text-muted-foreground">{text}</p>}
21
- </div>
22
- );
23
- }
24
-
25
- export function PageLoader({ text = 'Loading...' }: { text?: string }) {
26
- return (
27
- <div className="min-h-screen flex items-center justify-center">
28
- <LoadingSpinner size="lg" text={text} />
29
- </div>
30
- );
31
- }
32
-
33
- export function CardLoader() {
34
- return (
35
- <div className="p-12 flex items-center justify-center">
36
- <LoadingSpinner size="md" />
37
- </div>
38
- );
39
- }