@adeu/mcp-server 1.7.5 → 1.9.0
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.
- package/dist/assets/adeu.svg +3 -0
- package/dist/assets/logo.png +0 -0
- package/dist/assets/marked.min.js +69 -0
- package/dist/index.js +523 -395
- package/dist/index.js.map +1 -1
- package/dist/templates/email_ui.html +667 -0
- package/dist/templates/markdown_ui.html +745 -0
- package/package.json +4 -2
- package/src/assets/adeu.svg +3 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/marked.min.js +69 -0
- package/src/index.ts +510 -407
- package/src/mcp.cloud.test.ts +13 -0
- package/src/response-builders.ts +111 -50
- package/src/templates/email_ui.html +667 -0
- package/src/templates/markdown_ui.html +745 -0
- package/src/tools/email.ts +61 -2
- package/tsup.config.ts +35 -11
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
<!-- FILE: src/adeu/templates/email_ui.html -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html>
|
|
4
|
+
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="utf-8">
|
|
7
|
+
<title>Adeu Inbox</title>
|
|
8
|
+
<meta name="color-scheme" content="light dark">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link
|
|
12
|
+
href="https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600&family=Lora:ital,wght@0,600;1,600&display=swap"
|
|
13
|
+
rel="stylesheet">
|
|
14
|
+
<style>
|
|
15
|
+
:root {
|
|
16
|
+
--brand-sand: #e8c0a1;
|
|
17
|
+
--brand-navy: #1a2a2c;
|
|
18
|
+
--brand-navy-light: #2a3a3c;
|
|
19
|
+
--neutral-50: #fafafa;
|
|
20
|
+
--neutral-100: #f5f5f5;
|
|
21
|
+
--neutral-200: #e5e5e5;
|
|
22
|
+
--neutral-300: #d9d9d9;
|
|
23
|
+
--neutral-600: #555969;
|
|
24
|
+
--neutral-800: #1e2028;
|
|
25
|
+
--neutral-900: #171717;
|
|
26
|
+
--font-lora: 'Lora', Georgia, serif;
|
|
27
|
+
--font-jost: 'Jost', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
28
|
+
--bg-body: var(--neutral-50);
|
|
29
|
+
--bg-card: #ffffff;
|
|
30
|
+
--border-card: var(--neutral-200);
|
|
31
|
+
--text-main: var(--neutral-600);
|
|
32
|
+
--text-heading: var(--brand-navy);
|
|
33
|
+
--bg-hover: var(--neutral-100);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@media (prefers-color-scheme: dark) {
|
|
37
|
+
:root {
|
|
38
|
+
--bg-body: var(--neutral-900);
|
|
39
|
+
--bg-card: var(--brand-navy);
|
|
40
|
+
--border-card: var(--brand-navy-light);
|
|
41
|
+
--text-main: rgba(255, 255, 255, 0.85);
|
|
42
|
+
--text-heading: #ffffff;
|
|
43
|
+
--bg-hover: var(--brand-navy-light);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
html,
|
|
48
|
+
body {
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 0;
|
|
51
|
+
overflow: hidden !important;
|
|
52
|
+
box-sizing: border-box;
|
|
53
|
+
background-color: transparent;
|
|
54
|
+
height: 100%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
body {
|
|
58
|
+
font-family: var(--font-jost);
|
|
59
|
+
color: var(--text-main);
|
|
60
|
+
padding: 16px;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.adeu-card {
|
|
66
|
+
background-color: var(--bg-card);
|
|
67
|
+
border: 1px solid var(--border-card);
|
|
68
|
+
border-radius: 12px;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
display: flex;
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
max-height: 100%;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.navbar {
|
|
76
|
+
flex-shrink: 0;
|
|
77
|
+
background-color: var(--bg-card);
|
|
78
|
+
padding: 16px 32px;
|
|
79
|
+
border-bottom: 1px solid var(--border-card);
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.logo-container svg {
|
|
85
|
+
max-width: 80px;
|
|
86
|
+
height: auto;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@media (prefers-color-scheme: dark) {
|
|
90
|
+
|
|
91
|
+
.logo-container svg path,
|
|
92
|
+
.logo-container svg rect,
|
|
93
|
+
.logo-container svg polygon,
|
|
94
|
+
.logo-container svg circle {
|
|
95
|
+
fill: #ffffff !important;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.content-wrapper {
|
|
100
|
+
position: relative;
|
|
101
|
+
flex: 1 1 auto;
|
|
102
|
+
overflow-y: auto;
|
|
103
|
+
padding: 24px 32px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.content-wrapper::-webkit-scrollbar {
|
|
107
|
+
width: 6px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.content-wrapper::-webkit-scrollbar-thumb {
|
|
111
|
+
background-color: var(--border-card);
|
|
112
|
+
border-radius: 4px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* --- PREVIEW LIST VIEW --- */
|
|
116
|
+
.preview-list {
|
|
117
|
+
display: flex;
|
|
118
|
+
flex-direction: column;
|
|
119
|
+
gap: 12px;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.preview-item {
|
|
123
|
+
padding: 16px;
|
|
124
|
+
border: 1px solid var(--border-card);
|
|
125
|
+
border-radius: 8px;
|
|
126
|
+
background: var(--bg-card);
|
|
127
|
+
transition: border-color 0.2s ease, background 0.2s ease;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.preview-item:hover {
|
|
131
|
+
border-color: var(--brand-sand);
|
|
132
|
+
background: var(--bg-hover);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.preview-header {
|
|
136
|
+
display: flex;
|
|
137
|
+
justify-content: space-between;
|
|
138
|
+
align-items: flex-start;
|
|
139
|
+
margin-bottom: 8px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.preview-subject {
|
|
143
|
+
font-family: var(--font-jost);
|
|
144
|
+
font-size: 1rem;
|
|
145
|
+
color: var(--text-heading);
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
margin: 0;
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.preview-date {
|
|
154
|
+
font-size: 0.8rem;
|
|
155
|
+
color: var(--text-main);
|
|
156
|
+
opacity: 0.8;
|
|
157
|
+
white-space: nowrap;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.preview-sender {
|
|
161
|
+
font-size: 0.85rem;
|
|
162
|
+
font-weight: 500;
|
|
163
|
+
color: var(--text-main);
|
|
164
|
+
margin-bottom: 8px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.preview-snippet {
|
|
168
|
+
font-size: 0.85rem;
|
|
169
|
+
color: var(--text-main);
|
|
170
|
+
opacity: 0.8;
|
|
171
|
+
display: -webkit-box;
|
|
172
|
+
-webkit-line-clamp: 2;
|
|
173
|
+
-webkit-box-orient: vertical;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
text-overflow: ellipsis;
|
|
176
|
+
line-height: 1.4;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* --- FULL EMAIL VIEW --- */
|
|
180
|
+
.email-thread {
|
|
181
|
+
display: flex;
|
|
182
|
+
flex-direction: column;
|
|
183
|
+
gap: 24px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.email-message {
|
|
187
|
+
border: 1px solid var(--border-card);
|
|
188
|
+
border-radius: 8px;
|
|
189
|
+
background: var(--bg-card);
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.email-header {
|
|
194
|
+
padding: 16px;
|
|
195
|
+
border-bottom: 1px solid var(--border-card);
|
|
196
|
+
background: var(--bg-hover);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.email-subject {
|
|
200
|
+
font-family: var(--font-lora);
|
|
201
|
+
font-size: 1.25rem;
|
|
202
|
+
color: var(--text-heading);
|
|
203
|
+
margin: 0 0 8px 0;
|
|
204
|
+
font-weight: 600;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.email-meta {
|
|
208
|
+
display: flex;
|
|
209
|
+
justify-content: space-between;
|
|
210
|
+
align-items: flex-start;
|
|
211
|
+
font-size: 0.9rem;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.email-sender {
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
color: var(--text-heading);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.email-address {
|
|
220
|
+
color: var(--text-main);
|
|
221
|
+
opacity: 0.8;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.email-date {
|
|
225
|
+
font-size: 0.85rem;
|
|
226
|
+
color: var(--text-main);
|
|
227
|
+
opacity: 0.8;
|
|
228
|
+
white-space: nowrap;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.email-body-container {
|
|
232
|
+
padding: 24px 16px;
|
|
233
|
+
color: var(--text-main);
|
|
234
|
+
overflow-x: auto;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.attachments-section {
|
|
238
|
+
padding: 16px;
|
|
239
|
+
border-top: 1px dashed var(--border-card);
|
|
240
|
+
background: rgba(0, 0, 0, 0.02);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.attachments-title {
|
|
244
|
+
font-size: 0.85rem;
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
text-transform: uppercase;
|
|
247
|
+
letter-spacing: 0.05em;
|
|
248
|
+
margin-bottom: 12px;
|
|
249
|
+
display: flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
gap: 6px;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.attachment-list {
|
|
255
|
+
display: flex;
|
|
256
|
+
flex-direction: column;
|
|
257
|
+
gap: 8px;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.brief-section {
|
|
261
|
+
padding: 16px;
|
|
262
|
+
border-top: 2px solid var(--brand-sand);
|
|
263
|
+
background: rgba(232, 192, 161, 0.06);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.brief-title {
|
|
267
|
+
font-size: 0.85rem;
|
|
268
|
+
font-weight: 600;
|
|
269
|
+
text-transform: uppercase;
|
|
270
|
+
letter-spacing: 0.05em;
|
|
271
|
+
margin-bottom: 12px;
|
|
272
|
+
display: flex;
|
|
273
|
+
align-items: center;
|
|
274
|
+
gap: 6px;
|
|
275
|
+
color: var(--brand-sand);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.brief-body-container {
|
|
279
|
+
padding: 12px;
|
|
280
|
+
background: var(--bg-card);
|
|
281
|
+
border: 1px solid var(--border-card);
|
|
282
|
+
border-radius: 6px;
|
|
283
|
+
font-size: 0.9em;
|
|
284
|
+
color: var(--text-main);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.attachment-item {
|
|
288
|
+
display: flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
gap: 12px;
|
|
291
|
+
padding: 10px 12px;
|
|
292
|
+
background: var(--bg-card);
|
|
293
|
+
border: 1px solid var(--border-card);
|
|
294
|
+
border-radius: 6px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.attachment-icon {
|
|
298
|
+
color: var(--brand-sand);
|
|
299
|
+
flex-shrink: 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.attachment-info {
|
|
303
|
+
display: flex;
|
|
304
|
+
flex-direction: column;
|
|
305
|
+
overflow: hidden;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.attachment-name {
|
|
309
|
+
font-weight: 500;
|
|
310
|
+
font-size: 0.95rem;
|
|
311
|
+
color: var(--text-heading);
|
|
312
|
+
white-space: nowrap;
|
|
313
|
+
overflow: hidden;
|
|
314
|
+
text-overflow: ellipsis;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.attachment-path {
|
|
318
|
+
font-family: ui-monospace, Consolas, monospace;
|
|
319
|
+
font-size: 0.75rem;
|
|
320
|
+
color: var(--text-main);
|
|
321
|
+
opacity: 0.7;
|
|
322
|
+
white-space: nowrap;
|
|
323
|
+
overflow: hidden;
|
|
324
|
+
text-overflow: ellipsis;
|
|
325
|
+
margin-top: 2px;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.empty-state {
|
|
329
|
+
text-align: center;
|
|
330
|
+
padding: 40px 20px;
|
|
331
|
+
color: var(--text-main);
|
|
332
|
+
font-style: italic;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.skeleton {
|
|
336
|
+
background: linear-gradient(90deg, var(--neutral-100) 25%, var(--border-card) 50%, var(--neutral-100) 75%);
|
|
337
|
+
background-size: 200% 100%;
|
|
338
|
+
animation: skeleton 1.5s ease-in-out infinite;
|
|
339
|
+
border-radius: 4px;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
@keyframes skeleton {
|
|
343
|
+
0% {
|
|
344
|
+
background-position: 200% 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
100% {
|
|
348
|
+
background-position: -200% 0;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
</style>
|
|
352
|
+
</head>
|
|
353
|
+
|
|
354
|
+
<body>
|
|
355
|
+
<div class="adeu-card">
|
|
356
|
+
<nav class="navbar">
|
|
357
|
+
<div class="logo-container">[[ adeu_svg_code ]]</div>
|
|
358
|
+
</nav>
|
|
359
|
+
|
|
360
|
+
<div class="content-wrapper" id="content-wrapper">
|
|
361
|
+
<div id="app">
|
|
362
|
+
<div class="skeleton" style="height: 24px; width: 60%; margin-bottom: 12px;"></div>
|
|
363
|
+
<div class="skeleton" style="height: 16px; width: 40%; margin-bottom: 24px;"></div>
|
|
364
|
+
<div class="skeleton" style="height: 60px; width: 100%; margin-bottom: 12px;"></div>
|
|
365
|
+
<div class="skeleton" style="height: 60px; width: 100%;"></div>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<script>
|
|
371
|
+
const appDiv = document.getElementById('app');
|
|
372
|
+
const INIT_ID = 1;
|
|
373
|
+
|
|
374
|
+
function escapeHtml(unsafe) {
|
|
375
|
+
return (unsafe || "").toString()
|
|
376
|
+
.replace(/&/g, "&")
|
|
377
|
+
.replace(/</g, "<")
|
|
378
|
+
.replace(/>/g, ">")
|
|
379
|
+
.replace(/"/g, """)
|
|
380
|
+
.replace(/'/g, "'");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function formatBytes(bytes) {
|
|
384
|
+
if (bytes === 0 || !bytes) return '0 Bytes';
|
|
385
|
+
const k = 1024;
|
|
386
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
387
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
388
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function triggerResize() {
|
|
392
|
+
const nav = document.querySelector('.navbar');
|
|
393
|
+
const wrapper = document.getElementById('content-wrapper');
|
|
394
|
+
const desiredHeight = nav.offsetHeight + wrapper.scrollHeight + 34; // 34 is chrome padding
|
|
395
|
+
const requestedHeight = Math.min(desiredHeight, 800); // Max height 800px
|
|
396
|
+
|
|
397
|
+
window.parent.postMessage({
|
|
398
|
+
jsonrpc: "2.0",
|
|
399
|
+
method: "ui/notifications/size-changed",
|
|
400
|
+
params: { height: Math.ceil(requestedHeight), width: 650 }
|
|
401
|
+
}, "*");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const PAPERCLIP_SVG = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--brand-sand)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>`;
|
|
405
|
+
|
|
406
|
+
function renderPreviews(previews) {
|
|
407
|
+
if (!previews || previews.length === 0) {
|
|
408
|
+
return `<div class="empty-state">No emails found matching the search criteria.</div>`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let html = `<div class="preview-list">`;
|
|
412
|
+
previews.forEach(p => {
|
|
413
|
+
const dateStr = p.received_datetime ? new Date(p.received_datetime).toLocaleString() : '';
|
|
414
|
+
const attIcon = p.has_attachments ? PAPERCLIP_SVG : '';
|
|
415
|
+
|
|
416
|
+
const unreadDot = p.is_read === false ? `<span style="color: #3b82f6; margin-right: 6px; font-size: 1.2em; vertical-align: middle;">•</span>` : '';
|
|
417
|
+
|
|
418
|
+
html += `
|
|
419
|
+
<div class="preview-item">
|
|
420
|
+
<div class="preview-header">
|
|
421
|
+
<h4 class="preview-subject">${unreadDot}${escapeHtml(p.subject || 'No Subject')} ${attIcon}</h4>
|
|
422
|
+
<span class="preview-date">${escapeHtml(dateStr)}</span>
|
|
423
|
+
</div>
|
|
424
|
+
<div class="preview-sender">${escapeHtml(p.sender_name)} <${escapeHtml(p.sender_email)}></div>
|
|
425
|
+
<div class="preview-snippet">${escapeHtml(p.preview_text || 'No preview available')}</div>
|
|
426
|
+
</div>
|
|
427
|
+
`;
|
|
428
|
+
});
|
|
429
|
+
html += `</div>`;
|
|
430
|
+
return html;
|
|
431
|
+
}
|
|
432
|
+
function renderAttachmentsList(attachments) {
|
|
433
|
+
if (!attachments || attachments.length === 0) return '';
|
|
434
|
+
|
|
435
|
+
const itemsHtml = attachments.map(att => `
|
|
436
|
+
<div class="attachment-item">
|
|
437
|
+
${PAPERCLIP_SVG}
|
|
438
|
+
<div class="attachment-info">
|
|
439
|
+
<span class="attachment-name" title="${escapeHtml(att.filename)}">${escapeHtml(att.filename)} <span style="opacity:0.6; font-size:0.8em;">(${formatBytes(att.size_bytes)})</span></span>
|
|
440
|
+
${att.local_path ? `<span class="attachment-path" title="${escapeHtml(att.local_path)}">Local: ${escapeHtml(att.local_path)}</span>` : ''}
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
`).join('');
|
|
444
|
+
|
|
445
|
+
return `
|
|
446
|
+
<div class="attachments-section">
|
|
447
|
+
<div class="attachments-title">
|
|
448
|
+
${PAPERCLIP_SVG} Attachments (${attachments.length})
|
|
449
|
+
</div>
|
|
450
|
+
<div class="attachment-list">
|
|
451
|
+
${itemsHtml}
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
`;
|
|
455
|
+
}
|
|
456
|
+
function cleanEmailHTML(htmlString) {
|
|
457
|
+
if (!htmlString) return '';
|
|
458
|
+
const parser = new DOMParser();
|
|
459
|
+
const doc = parser.parseFromString(htmlString, 'text/html');
|
|
460
|
+
|
|
461
|
+
// Prune standard nested quotes to keep the UI clean
|
|
462
|
+
const selectorsToPrune = [
|
|
463
|
+
'.gmail_quote', // Gmail
|
|
464
|
+
'blockquote[type="cite"]', // Apple Mail
|
|
465
|
+
'#divRplyFwdMsg', // Outlook Desktop
|
|
466
|
+
'#appendonsend', // Outlook Web
|
|
467
|
+
'.ydp_quote' // Yahoo
|
|
468
|
+
];
|
|
469
|
+
|
|
470
|
+
selectorsToPrune.forEach(selector => {
|
|
471
|
+
doc.querySelectorAll(selector).forEach(el => el.remove());
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Handle Outlook's <hr tabindex="-1"> pattern
|
|
475
|
+
const hrs = doc.querySelectorAll('hr');
|
|
476
|
+
hrs.forEach(hr => {
|
|
477
|
+
if (hr.getAttribute('tabindex') === '-1' || hr.style.display === 'inline-block') {
|
|
478
|
+
// Outlook often puts the quote text right after this HR.
|
|
479
|
+
// We remove all siblings after this hr.
|
|
480
|
+
let next = hr.nextSibling;
|
|
481
|
+
while (next) {
|
|
482
|
+
const toRemove = next;
|
|
483
|
+
next = next.nextSibling;
|
|
484
|
+
toRemove.remove();
|
|
485
|
+
}
|
|
486
|
+
hr.remove();
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
return doc.body.innerHTML;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function renderFullEmail(email) {
|
|
494
|
+
const dateStr = email.received_datetime ? new Date(email.received_datetime).toLocaleString() : '';
|
|
495
|
+
|
|
496
|
+
const attachmentsHtml = renderAttachmentsList(email.attachments);
|
|
497
|
+
const wrapper = document.createElement('div');
|
|
498
|
+
wrapper.className = 'email-thread';
|
|
499
|
+
|
|
500
|
+
let threadHtml = '';
|
|
501
|
+
|
|
502
|
+
// 1. Render Target Email FIRST
|
|
503
|
+
let briefHtml = '';
|
|
504
|
+
if (email.brief_content) {
|
|
505
|
+
briefHtml = `
|
|
506
|
+
<div class="brief-section">
|
|
507
|
+
<div class="brief-title">
|
|
508
|
+
🧠 AI Strategy Brief
|
|
509
|
+
</div>
|
|
510
|
+
<div class="brief-body-container" id="brief-${email.id}"></div>
|
|
511
|
+
</div>
|
|
512
|
+
`;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
threadHtml += `
|
|
516
|
+
<div class="email-message" style="box-shadow: 0 4px 12px rgba(0,0,0,0.05); border-color: var(--brand-sand);">
|
|
517
|
+
<div class="email-header">
|
|
518
|
+
<h3 class="email-subject">${escapeHtml(email.subject || 'No Subject')}</h3>
|
|
519
|
+
<div class="email-meta">
|
|
520
|
+
<div>
|
|
521
|
+
<span class="email-sender">${escapeHtml(email.sender_name || 'Unknown')}</span>
|
|
522
|
+
<span class="email-address"><${escapeHtml(email.sender_email)}></span>
|
|
523
|
+
</div>
|
|
524
|
+
<div class="email-date">${escapeHtml(dateStr)}</div>
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
<div class="email-body-container" id="body-${email.id}"></div>
|
|
528
|
+
${attachmentsHtml}
|
|
529
|
+
${briefHtml}
|
|
530
|
+
</div>
|
|
531
|
+
`;
|
|
532
|
+
|
|
533
|
+
// 2. Render Thread History BELOW target email
|
|
534
|
+
if (email.is_thread && email.messages && email.messages.length > 0) {
|
|
535
|
+
threadHtml += `<div style="margin-top: 16px; margin-bottom: -12px; font-weight: 600; color: var(--text-main); font-size: 0.9em; text-transform: uppercase; letter-spacing: 0.05em; padding-left: 8px;">Previous Messages</div>`;
|
|
536
|
+
|
|
537
|
+
email.messages.forEach((histMsg, index) => {
|
|
538
|
+
const histDate = histMsg.received_datetime ? new Date(histMsg.received_datetime).toLocaleString() : '';
|
|
539
|
+
const histAttachmentsHtml = renderAttachmentsList(histMsg.attachments);
|
|
540
|
+
|
|
541
|
+
threadHtml += `
|
|
542
|
+
<div class="email-message" style="opacity: 0.85; transform: scale(0.98); transform-origin: top;">
|
|
543
|
+
<div class="email-header" style="padding: 12px 16px; background: rgba(0,0,0,0.02);">
|
|
544
|
+
<div class="email-meta">
|
|
545
|
+
<div>
|
|
546
|
+
<span class="email-sender">${escapeHtml(histMsg.sender_name || 'Unknown')}</span>
|
|
547
|
+
<span class="email-address"><${escapeHtml(histMsg.sender_email)}></span>
|
|
548
|
+
</div>
|
|
549
|
+
<div class="email-date">${escapeHtml(histDate)}</div>
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
<div class="email-body-container" id="history-body-${index}-${email.id}" style="max-height: 250px; overflow-y: auto; padding: 16px;"></div>
|
|
553
|
+
${histAttachmentsHtml}
|
|
554
|
+
</div>
|
|
555
|
+
`;
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
wrapper.innerHTML = threadHtml;
|
|
560
|
+
return wrapper;
|
|
561
|
+
}
|
|
562
|
+
window.addEventListener("message", (event) => {
|
|
563
|
+
try {
|
|
564
|
+
if (!event.data || event.data.jsonrpc !== "2.0") return;
|
|
565
|
+
const msg = event.data;
|
|
566
|
+
|
|
567
|
+
if (msg.id === INIT_ID) {
|
|
568
|
+
window.parent.postMessage({ jsonrpc: "2.0", method: "ui/notifications/initialized", params: {} }, "*");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (msg.method === "ui/notifications/tool-result") {
|
|
573
|
+
const result = msg.params;
|
|
574
|
+
if (!result || !result.structuredContent) return;
|
|
575
|
+
|
|
576
|
+
const data = result.structuredContent;
|
|
577
|
+
appDiv.innerHTML = ''; // Clear skeleton
|
|
578
|
+
|
|
579
|
+
if (data.type === "previews") {
|
|
580
|
+
appDiv.innerHTML = renderPreviews(data.previews);
|
|
581
|
+
setTimeout(triggerResize, 50);
|
|
582
|
+
}
|
|
583
|
+
else if (data.type === "full_email" && data.full_email) {
|
|
584
|
+
const email = data.full_email;
|
|
585
|
+
const emailEl = renderFullEmail(email);
|
|
586
|
+
appDiv.appendChild(emailEl);
|
|
587
|
+
|
|
588
|
+
// Inject Target Message Body
|
|
589
|
+
const bodyContainer = document.getElementById(`body-${email.id}`);
|
|
590
|
+
if (bodyContainer) {
|
|
591
|
+
const cleanedBody = cleanEmailHTML(email.body_html);
|
|
592
|
+
const shadow = bodyContainer.attachShadow({ mode: 'open' });
|
|
593
|
+
shadow.innerHTML = `
|
|
594
|
+
<style>
|
|
595
|
+
:host { all: initial; display: block; font-family: system-ui, sans-serif; line-height: 1.5; color: var(--text-main, #333); }
|
|
596
|
+
@media (prefers-color-scheme: dark) { :host { color: #e5e5e5; } a { color: #60a5fa; } }
|
|
597
|
+
img { max-width: 100%; height: auto; }
|
|
598
|
+
a { target-name: new; target-new: window; }
|
|
599
|
+
</style>
|
|
600
|
+
<div>${cleanedBody || '<em>Empty Message</em>'}</div>
|
|
601
|
+
`;
|
|
602
|
+
}
|
|
603
|
+
// Inject Brief Content (styled HTML from DB)
|
|
604
|
+
if (email.brief_content) {
|
|
605
|
+
const briefContainer = document.getElementById(`brief-${email.id}`);
|
|
606
|
+
if (briefContainer) {
|
|
607
|
+
const shadow = briefContainer.attachShadow({ mode: 'open' });
|
|
608
|
+
shadow.innerHTML = `
|
|
609
|
+
<style>
|
|
610
|
+
:host { all: initial; display: block; font-family: system-ui, sans-serif; line-height: 1.6; color: var(--text-main, #333); }
|
|
611
|
+
@media (prefers-color-scheme: dark) { :host { color: #e5e5e5; } a { color: #60a5fa; } }
|
|
612
|
+
img { max-width: 100%; height: auto; }
|
|
613
|
+
table { width: 100%; border-collapse: collapse; }
|
|
614
|
+
td, th { padding: 4px 8px; }
|
|
615
|
+
</style>
|
|
616
|
+
<div>${email.brief_content}</div>
|
|
617
|
+
`;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// Inject Thread History Bodies
|
|
621
|
+
if (email.is_thread && email.messages) {
|
|
622
|
+
email.messages.forEach((histMsg, index) => {
|
|
623
|
+
const histContainer = document.getElementById(`history-body-${index}-${email.id}`);
|
|
624
|
+
if (histContainer) {
|
|
625
|
+
const cleanedHist = cleanEmailHTML(histMsg.body_html);
|
|
626
|
+
const shadow = histContainer.attachShadow({ mode: 'open' });
|
|
627
|
+
shadow.innerHTML = `
|
|
628
|
+
<style>
|
|
629
|
+
:host { all: initial; display: block; font-family: system-ui, sans-serif; line-height: 1.5; color: var(--text-main, #333); font-size: 0.9em; }
|
|
630
|
+
@media (prefers-color-scheme: dark) { :host { color: #e5e5e5; } a { color: #60a5fa; } }
|
|
631
|
+
img { max-width: 100%; height: auto; }
|
|
632
|
+
a { target-name: new; target-new: window; }
|
|
633
|
+
</style>
|
|
634
|
+
<div>${cleanedHist || '<em>Empty Message</em>'}</div>
|
|
635
|
+
`;
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
setTimeout(triggerResize, 100);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
appDiv.innerHTML = `<div class="empty-state">No email data found.</div>`;
|
|
644
|
+
triggerResize();
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
} catch (error) {
|
|
648
|
+
console.error("UI Render Error:", error);
|
|
649
|
+
appDiv.innerHTML = `<div class="empty-state" style="color: red;">Error rendering UI: ${error.toString()}</div>`;
|
|
650
|
+
triggerResize();
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const observer = new ResizeObserver(() => triggerResize());
|
|
655
|
+
observer.observe(document.getElementById('content-wrapper'));
|
|
656
|
+
|
|
657
|
+
window.parent.postMessage({
|
|
658
|
+
jsonrpc: "2.0", id: INIT_ID, method: "ui/initialize",
|
|
659
|
+
params: {
|
|
660
|
+
appInfo: { name: "Adeu Email Inbox", version: "1.0.0" },
|
|
661
|
+
appCapabilities: {}, protocolVersion: "2025-11-21"
|
|
662
|
+
}
|
|
663
|
+
}, "*");
|
|
664
|
+
</script>
|
|
665
|
+
</body>
|
|
666
|
+
|
|
667
|
+
</html>
|