clay-server 2.30.0 → 2.31.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/lib/email-accounts.js +299 -0
- package/lib/email-mcp-server.js +646 -0
- package/lib/project-connection.js +26 -2
- package/lib/project-email.js +418 -0
- package/lib/project-sessions.js +16 -0
- package/lib/project-user-message.js +26 -5
- package/lib/project.js +72 -25
- package/lib/public/app.js +18 -5
- package/lib/public/css/filebrowser.css +80 -2
- package/lib/public/css/input.css +196 -0
- package/lib/public/css/notifications-center.css +3 -0
- package/lib/public/css/sidebar.css +77 -2
- package/lib/public/css/sticky-notes.css +0 -48
- package/lib/public/css/user-settings.css +85 -0
- package/lib/public/icons/email/gmail.svg +7 -0
- package/lib/public/icons/email/outlook.svg +35 -0
- package/lib/public/icons/email/yahoo.svg +1 -0
- package/lib/public/index.html +36 -3
- package/lib/public/modules/app-dm.js +4 -9
- package/lib/public/modules/app-messages.js +37 -2
- package/lib/public/modules/app-panels.js +2 -1
- package/lib/public/modules/context-sources.js +527 -1
- package/lib/public/modules/filebrowser.js +72 -0
- package/lib/public/modules/mate-sidebar.js +7 -0
- package/lib/public/modules/sidebar-mobile.js +1 -1
- package/lib/public/modules/sidebar.js +144 -2
- package/lib/public/modules/sticky-notes.js +1 -91
- package/lib/public/modules/terminal.js +0 -12
- package/lib/public/modules/theme.js +4 -0
- package/lib/public/modules/tools.js +23 -0
- package/lib/public/modules/user-settings.js +74 -0
- package/lib/sdk-bridge.js +16 -0
- package/lib/sdk-message-processor.js +33 -0
- package/lib/server-email.js +148 -0
- package/lib/server.js +5 -0
- package/package.json +3 -2
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
// Context Sources — attach terminal output
|
|
1
|
+
// Context Sources — attach terminal output, browser tabs, and email accounts as context for Claude
|
|
2
2
|
|
|
3
3
|
var ctx = null;
|
|
4
4
|
var activeSourceIds = new Set();
|
|
5
5
|
var terminalList = []; // synced from terminal module's term_list
|
|
6
6
|
var browserTabList = []; // synced from Chrome extension via postMessage
|
|
7
|
+
var emailAccountList = []; // synced from server via email_accounts_list
|
|
8
|
+
var emailProviders = {}; // provider presets from server
|
|
9
|
+
var emailUnreadCounts = {}; // accountId -> unread count
|
|
10
|
+
var _emailTestPassed = false;
|
|
11
|
+
var _emailPendingSave = false;
|
|
12
|
+
var _emailDoSave = null; // set by showEmailSetupModal
|
|
13
|
+
var emailDefaultAccountIds = []; // project-level defaults
|
|
7
14
|
|
|
8
15
|
export function initContextSources(_ctx) {
|
|
9
16
|
ctx = _ctx;
|
|
@@ -110,6 +117,47 @@ export function updateBrowserTabList(tabs) {
|
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
|
|
120
|
+
// Called when email_accounts_list arrives from server
|
|
121
|
+
export function updateEmailAccountList(msg) {
|
|
122
|
+
emailAccountList = msg.accounts || [];
|
|
123
|
+
if (msg.providers) emailProviders = msg.providers;
|
|
124
|
+
|
|
125
|
+
// Remove active email sources that no longer exist
|
|
126
|
+
var changed = false;
|
|
127
|
+
for (var id of activeSourceIds) {
|
|
128
|
+
if (id.startsWith("email:")) {
|
|
129
|
+
var accId = id.split(":")[1];
|
|
130
|
+
var found = false;
|
|
131
|
+
for (var i = 0; i < emailAccountList.length; i++) {
|
|
132
|
+
if (emailAccountList[i].id === accId) { found = true; break; }
|
|
133
|
+
}
|
|
134
|
+
if (!found) {
|
|
135
|
+
activeSourceIds.delete(id);
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (changed) saveToServer();
|
|
142
|
+
renderChips();
|
|
143
|
+
|
|
144
|
+
var picker = document.getElementById("context-sources-picker");
|
|
145
|
+
if (!picker.classList.contains("hidden")) {
|
|
146
|
+
renderPicker();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Called when email_unread_update arrives from server
|
|
151
|
+
export function updateEmailUnreadCounts(msg) {
|
|
152
|
+
emailUnreadCounts = msg.unread || {};
|
|
153
|
+
renderChips();
|
|
154
|
+
|
|
155
|
+
var picker = document.getElementById("context-sources-picker");
|
|
156
|
+
if (!picker.classList.contains("hidden")) {
|
|
157
|
+
renderPicker();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
113
161
|
function toggleSource(sourceId) {
|
|
114
162
|
if (activeSourceIds.has(sourceId)) {
|
|
115
163
|
activeSourceIds.delete(sourceId);
|
|
@@ -279,9 +327,387 @@ function renderPicker() {
|
|
|
279
327
|
}
|
|
280
328
|
}
|
|
281
329
|
|
|
330
|
+
// --- Email Accounts section ---
|
|
331
|
+
var emailSection = document.getElementById("context-picker-email");
|
|
332
|
+
emailSection.innerHTML = "";
|
|
333
|
+
|
|
334
|
+
var emailLabel = document.createElement("div");
|
|
335
|
+
emailLabel.className = "context-picker-section-label";
|
|
336
|
+
emailLabel.textContent = "Email Accounts";
|
|
337
|
+
emailSection.appendChild(emailLabel);
|
|
338
|
+
|
|
339
|
+
if (emailAccountList.length === 0) {
|
|
340
|
+
var emailEmpty = document.createElement("div");
|
|
341
|
+
emailEmpty.className = "context-picker-empty";
|
|
342
|
+
emailEmpty.textContent = "No email accounts connected";
|
|
343
|
+
emailSection.appendChild(emailEmpty);
|
|
344
|
+
|
|
345
|
+
var addAccBtn = document.createElement("div");
|
|
346
|
+
addAccBtn.className = "context-picker-item context-picker-add-item";
|
|
347
|
+
addAccBtn.style.justifyContent = "center";
|
|
348
|
+
addAccBtn.innerHTML = '<i data-lucide="mail-plus"></i><span>Add Account</span>';
|
|
349
|
+
addAccBtn.addEventListener("click", function (e) {
|
|
350
|
+
e.stopPropagation();
|
|
351
|
+
closePicker();
|
|
352
|
+
showEmailSetupModal();
|
|
353
|
+
});
|
|
354
|
+
emailSection.appendChild(addAccBtn);
|
|
355
|
+
} else {
|
|
356
|
+
for (var k = 0; k < emailAccountList.length; k++) {
|
|
357
|
+
var acc = emailAccountList[k];
|
|
358
|
+
var emailSourceId = "email:" + acc.id;
|
|
359
|
+
var emailActive = activeSourceIds.has(emailSourceId);
|
|
360
|
+
var unreadCount = emailUnreadCounts[acc.id] || 0;
|
|
361
|
+
|
|
362
|
+
var emailItem = document.createElement("div");
|
|
363
|
+
emailItem.className = "context-picker-item" + (emailActive ? " active" : "");
|
|
364
|
+
emailItem.setAttribute("data-source-id", emailSourceId);
|
|
365
|
+
|
|
366
|
+
var unreadBadge = unreadCount > 0
|
|
367
|
+
? '<span class="context-picker-unread-badge">' + unreadCount + '</span>'
|
|
368
|
+
: '';
|
|
369
|
+
|
|
370
|
+
emailItem.innerHTML =
|
|
371
|
+
'<i data-lucide="mail"></i>' +
|
|
372
|
+
'<span>' + escapeHtml(acc.email) + '</span>' +
|
|
373
|
+
unreadBadge +
|
|
374
|
+
'<i data-lucide="check" class="context-picker-check"></i>';
|
|
375
|
+
|
|
376
|
+
emailItem.addEventListener("click", function() {
|
|
377
|
+
toggleSource(this.getAttribute("data-source-id"));
|
|
378
|
+
if (typeof lucide !== "undefined") lucide.createIcons();
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
emailSection.appendChild(emailItem);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// "Add Account" link at the bottom
|
|
385
|
+
var addMoreBtn = document.createElement("div");
|
|
386
|
+
addMoreBtn.className = "context-picker-item context-picker-add-item";
|
|
387
|
+
addMoreBtn.innerHTML = '<i data-lucide="plus"></i><span>Add Email Account</span>';
|
|
388
|
+
addMoreBtn.addEventListener("click", function () {
|
|
389
|
+
closePicker();
|
|
390
|
+
showEmailSetupModal();
|
|
391
|
+
});
|
|
392
|
+
emailSection.appendChild(addMoreBtn);
|
|
393
|
+
}
|
|
394
|
+
|
|
282
395
|
if (typeof lucide !== "undefined") lucide.createIcons();
|
|
283
396
|
}
|
|
284
397
|
|
|
398
|
+
// --- Email Setup Modal ---
|
|
399
|
+
|
|
400
|
+
export function getEmailAccountListCache() {
|
|
401
|
+
return emailAccountList;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function showEmailSetupModal() {
|
|
405
|
+
// Remove any existing modal
|
|
406
|
+
var existing = document.getElementById("email-setup-modal");
|
|
407
|
+
if (existing) existing.remove();
|
|
408
|
+
|
|
409
|
+
var overlay = document.createElement("div");
|
|
410
|
+
overlay.id = "email-setup-modal";
|
|
411
|
+
overlay.className = "email-setup-overlay";
|
|
412
|
+
|
|
413
|
+
var providerOptions = '<option value="gmail">Gmail</option><option value="outlook">Outlook</option><option value="yahoo">Yahoo</option><option value="custom">Custom</option>';
|
|
414
|
+
|
|
415
|
+
var modal = document.createElement("div");
|
|
416
|
+
modal.className = "email-setup-dialog";
|
|
417
|
+
modal.innerHTML =
|
|
418
|
+
'<h3 class="email-setup-title">Add Email Account</h3>' +
|
|
419
|
+
'<div class="email-setup-field">' +
|
|
420
|
+
'<label class="email-setup-label">Provider</label>' +
|
|
421
|
+
'<select id="email-setup-provider" class="email-setup-input">' + providerOptions + '</select>' +
|
|
422
|
+
'</div>' +
|
|
423
|
+
'<div class="email-setup-field">' +
|
|
424
|
+
'<label class="email-setup-label">Email Address</label>' +
|
|
425
|
+
'<div class="email-setup-email-wrap">' +
|
|
426
|
+
'<input id="email-setup-address" type="text" placeholder="you" class="email-setup-input email-setup-email-input" />' +
|
|
427
|
+
'<span id="email-setup-domain" class="email-setup-domain">@gmail.com</span>' +
|
|
428
|
+
'</div>' +
|
|
429
|
+
'</div>' +
|
|
430
|
+
'<div class="email-setup-field">' +
|
|
431
|
+
'<label class="email-setup-label">App Password</label>' +
|
|
432
|
+
'<div class="email-setup-password-wrap">' +
|
|
433
|
+
'<input id="email-setup-password" type="password" placeholder="xxxx-xxxx-xxxx-xxxx" class="email-setup-input" />' +
|
|
434
|
+
'<button id="email-setup-password-toggle" type="button" class="email-setup-password-eye" title="Show password"><i data-lucide="eye"></i></button>' +
|
|
435
|
+
'</div>' +
|
|
436
|
+
'<a id="email-setup-help" href="https://support.google.com/accounts/answer/185833" target="_blank" rel="noopener" class="email-setup-help">How to create an App Password</a>' +
|
|
437
|
+
'</div>' +
|
|
438
|
+
'<div id="email-setup-custom-fields" style="display:none;">' +
|
|
439
|
+
'<div class="email-setup-field">' +
|
|
440
|
+
'<label class="email-setup-label">IMAP Host</label>' +
|
|
441
|
+
'<div class="email-setup-row">' +
|
|
442
|
+
'<input id="email-setup-imap-host" type="text" placeholder="imap.example.com" class="email-setup-input" style="flex:1;" />' +
|
|
443
|
+
'<input id="email-setup-imap-port" type="number" value="993" class="email-setup-input email-setup-port" />' +
|
|
444
|
+
'</div>' +
|
|
445
|
+
'</div>' +
|
|
446
|
+
'<div class="email-setup-field">' +
|
|
447
|
+
'<label class="email-setup-label">SMTP Host</label>' +
|
|
448
|
+
'<div class="email-setup-row">' +
|
|
449
|
+
'<input id="email-setup-smtp-host" type="text" placeholder="smtp.example.com" class="email-setup-input" style="flex:1;" />' +
|
|
450
|
+
'<input id="email-setup-smtp-port" type="number" value="587" class="email-setup-input email-setup-port" />' +
|
|
451
|
+
'</div>' +
|
|
452
|
+
'</div>' +
|
|
453
|
+
'</div>' +
|
|
454
|
+
'<div id="email-setup-status" class="email-setup-status"></div>' +
|
|
455
|
+
'<div class="email-setup-actions">' +
|
|
456
|
+
'<button id="email-setup-test" type="button" class="email-setup-btn email-setup-btn-secondary">Test Connection</button>' +
|
|
457
|
+
'<button id="email-setup-cancel" type="button" class="email-setup-btn email-setup-btn-ghost">Cancel</button>' +
|
|
458
|
+
'<button id="email-setup-save" type="button" class="email-setup-btn email-setup-btn-primary">Add Account</button>' +
|
|
459
|
+
'</div>';
|
|
460
|
+
|
|
461
|
+
overlay.appendChild(modal);
|
|
462
|
+
document.body.appendChild(overlay);
|
|
463
|
+
|
|
464
|
+
// Event: close on overlay click
|
|
465
|
+
overlay.addEventListener("click", function (e) {
|
|
466
|
+
if (e.target === overlay) overlay.remove();
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Event: cancel button
|
|
470
|
+
document.getElementById("email-setup-cancel").addEventListener("click", function () {
|
|
471
|
+
overlay.remove();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Event: password visibility toggle
|
|
475
|
+
var pwInput = document.getElementById("email-setup-password");
|
|
476
|
+
var pwToggle = document.getElementById("email-setup-password-toggle");
|
|
477
|
+
pwToggle.addEventListener("click", function () {
|
|
478
|
+
var isHidden = pwInput.type === "password";
|
|
479
|
+
pwInput.type = isHidden ? "text" : "password";
|
|
480
|
+
pwToggle.innerHTML = isHidden ? '<i data-lucide="eye-off"></i>' : '<i data-lucide="eye"></i>';
|
|
481
|
+
pwToggle.title = isHidden ? "Hide password" : "Show password";
|
|
482
|
+
if (typeof lucide !== "undefined") lucide.createIcons();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Event: provider change
|
|
486
|
+
var providerSelect = document.getElementById("email-setup-provider");
|
|
487
|
+
var helpLink = document.getElementById("email-setup-help");
|
|
488
|
+
var customFields = document.getElementById("email-setup-custom-fields");
|
|
489
|
+
|
|
490
|
+
var providerMeta = {
|
|
491
|
+
gmail: {
|
|
492
|
+
domain: "@gmail.com",
|
|
493
|
+
placeholder: "you",
|
|
494
|
+
helpUrl: "https://support.google.com/accounts/answer/185833",
|
|
495
|
+
},
|
|
496
|
+
outlook: {
|
|
497
|
+
domain: "@outlook.com",
|
|
498
|
+
placeholder: "you",
|
|
499
|
+
helpUrl: "https://support.microsoft.com/en-us/account-billing/using-app-passwords-with-apps-that-don-t-support-two-step-verification-5896ed9b-4263-e681-128a-a6f2979a7944",
|
|
500
|
+
},
|
|
501
|
+
yahoo: {
|
|
502
|
+
domain: "@yahoo.com",
|
|
503
|
+
placeholder: "you",
|
|
504
|
+
helpUrl: "https://help.yahoo.com/kb/generate-manage-third-party-passwords-sln15241.html",
|
|
505
|
+
},
|
|
506
|
+
custom: {
|
|
507
|
+
domain: null,
|
|
508
|
+
placeholder: "you@example.com",
|
|
509
|
+
helpUrl: null,
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
var emailInput = document.getElementById("email-setup-address");
|
|
514
|
+
var domainSuffix = document.getElementById("email-setup-domain");
|
|
515
|
+
|
|
516
|
+
function applyProviderMeta(val) {
|
|
517
|
+
var meta = providerMeta[val] || providerMeta.custom;
|
|
518
|
+
customFields.style.display = val === "custom" ? "block" : "none";
|
|
519
|
+
emailInput.placeholder = meta.placeholder;
|
|
520
|
+
if (meta.domain) {
|
|
521
|
+
domainSuffix.textContent = meta.domain;
|
|
522
|
+
domainSuffix.style.display = "";
|
|
523
|
+
emailInput.type = "text";
|
|
524
|
+
} else {
|
|
525
|
+
domainSuffix.style.display = "none";
|
|
526
|
+
emailInput.type = "email";
|
|
527
|
+
}
|
|
528
|
+
if (meta.helpUrl) {
|
|
529
|
+
helpLink.href = meta.helpUrl;
|
|
530
|
+
helpLink.style.display = "";
|
|
531
|
+
} else {
|
|
532
|
+
helpLink.style.display = "none";
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
providerSelect.addEventListener("change", function () {
|
|
537
|
+
applyProviderMeta(providerSelect.value);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Strip @ and everything after it for known providers
|
|
541
|
+
emailInput.addEventListener("input", function () {
|
|
542
|
+
var meta = providerMeta[providerSelect.value];
|
|
543
|
+
if (meta && meta.domain) {
|
|
544
|
+
var val = emailInput.value;
|
|
545
|
+
var atIdx = val.indexOf("@");
|
|
546
|
+
if (atIdx !== -1) {
|
|
547
|
+
emailInput.value = val.substring(0, atIdx);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// Reset test state
|
|
553
|
+
_emailTestPassed = false;
|
|
554
|
+
_emailPendingSave = false;
|
|
555
|
+
|
|
556
|
+
// Reset test status when inputs change
|
|
557
|
+
function onInputChange() {
|
|
558
|
+
_emailTestPassed = false;
|
|
559
|
+
_emailPendingSave = false;
|
|
560
|
+
var saveBtn = document.getElementById("email-setup-save");
|
|
561
|
+
if (saveBtn) saveBtn.textContent = "Add Account";
|
|
562
|
+
}
|
|
563
|
+
document.getElementById("email-setup-address").addEventListener("input", onInputChange);
|
|
564
|
+
document.getElementById("email-setup-password").addEventListener("input", onInputChange);
|
|
565
|
+
providerSelect.addEventListener("change", onInputChange);
|
|
566
|
+
|
|
567
|
+
function runTest() {
|
|
568
|
+
var statusEl = document.getElementById("email-setup-status");
|
|
569
|
+
var emailAddr = getFullEmail();
|
|
570
|
+
var appPass = document.getElementById("email-setup-password").value;
|
|
571
|
+
|
|
572
|
+
if (!emailAddr || emailAddr.indexOf("@") === -1 || !appPass) {
|
|
573
|
+
statusEl.style.display = "block";
|
|
574
|
+
statusEl.style.color = "var(--error, #e74c3c)";
|
|
575
|
+
statusEl.textContent = "Email and app password are required.";
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
statusEl.style.display = "block";
|
|
580
|
+
statusEl.style.color = "var(--text-muted)";
|
|
581
|
+
statusEl.textContent = "Testing connection...";
|
|
582
|
+
|
|
583
|
+
var msgData = buildEmailSetupData();
|
|
584
|
+
msgData.type = "email_account_test";
|
|
585
|
+
|
|
586
|
+
if (ctx && ctx.ws && ctx.connected) {
|
|
587
|
+
ctx.ws.send(JSON.stringify(msgData));
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Event: test connection
|
|
592
|
+
document.getElementById("email-setup-test").addEventListener("click", function () {
|
|
593
|
+
_emailPendingSave = false;
|
|
594
|
+
runTest();
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// Event: save (requires test to pass first)
|
|
598
|
+
document.getElementById("email-setup-save").addEventListener("click", function () {
|
|
599
|
+
if (_emailTestPassed) {
|
|
600
|
+
// Test already passed, proceed with save
|
|
601
|
+
doSave();
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
// Run test first, then auto-save on success
|
|
605
|
+
_emailPendingSave = true;
|
|
606
|
+
var saveBtn = document.getElementById("email-setup-save");
|
|
607
|
+
if (saveBtn) saveBtn.textContent = "Testing...";
|
|
608
|
+
runTest();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
_emailDoSave = doSave;
|
|
612
|
+
function doSave() {
|
|
613
|
+
var statusEl = document.getElementById("email-setup-status");
|
|
614
|
+
statusEl.style.display = "block";
|
|
615
|
+
statusEl.style.color = "var(--text-muted)";
|
|
616
|
+
statusEl.textContent = "Adding account...";
|
|
617
|
+
|
|
618
|
+
var msgData = buildEmailSetupData();
|
|
619
|
+
msgData.type = "email_account_add";
|
|
620
|
+
|
|
621
|
+
if (ctx && ctx.ws && ctx.connected) {
|
|
622
|
+
ctx.ws.send(JSON.stringify(msgData));
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function getFullEmail() {
|
|
628
|
+
var provider = document.getElementById("email-setup-provider").value;
|
|
629
|
+
var raw = document.getElementById("email-setup-address").value.trim();
|
|
630
|
+
var meta = { gmail: "@gmail.com", outlook: "@outlook.com", yahoo: "@yahoo.com" };
|
|
631
|
+
if (meta[provider] && raw.indexOf("@") === -1) {
|
|
632
|
+
return raw + meta[provider];
|
|
633
|
+
}
|
|
634
|
+
return raw;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function buildEmailSetupData() {
|
|
638
|
+
var provider = document.getElementById("email-setup-provider").value;
|
|
639
|
+
var email = getFullEmail();
|
|
640
|
+
var appPassword = document.getElementById("email-setup-password").value;
|
|
641
|
+
var data = { provider: provider, email: email, appPassword: appPassword };
|
|
642
|
+
|
|
643
|
+
if (provider === "custom") {
|
|
644
|
+
data.imap = {
|
|
645
|
+
host: document.getElementById("email-setup-imap-host").value.trim(),
|
|
646
|
+
port: parseInt(document.getElementById("email-setup-imap-port").value, 10) || 993,
|
|
647
|
+
tls: true,
|
|
648
|
+
};
|
|
649
|
+
data.smtp = {
|
|
650
|
+
host: document.getElementById("email-setup-smtp-host").value.trim(),
|
|
651
|
+
port: parseInt(document.getElementById("email-setup-smtp-port").value, 10) || 587,
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
return data;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Handle email_account_test_result from server
|
|
658
|
+
export function handleEmailTestResult(msg) {
|
|
659
|
+
var statusEl = document.getElementById("email-setup-status");
|
|
660
|
+
if (!statusEl) return;
|
|
661
|
+
var saveBtn = document.getElementById("email-setup-save");
|
|
662
|
+
|
|
663
|
+
statusEl.style.display = "block";
|
|
664
|
+
if (msg.ok) {
|
|
665
|
+
_emailTestPassed = true;
|
|
666
|
+
if (_emailPendingSave && _emailDoSave) {
|
|
667
|
+
// Test passed from Add Account click, proceed to save
|
|
668
|
+
_emailPendingSave = false;
|
|
669
|
+
_emailDoSave();
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
statusEl.style.color = "var(--success, #2ecc71)";
|
|
673
|
+
statusEl.textContent = "Connection successful! IMAP and SMTP both working.";
|
|
674
|
+
} else {
|
|
675
|
+
_emailTestPassed = false;
|
|
676
|
+
_emailPendingSave = false;
|
|
677
|
+
if (saveBtn) saveBtn.textContent = "Add Account";
|
|
678
|
+
statusEl.style.color = "var(--error, #e74c3c)";
|
|
679
|
+
var parts = [];
|
|
680
|
+
if (msg.imap && !msg.imap.ok) parts.push("IMAP: " + (msg.imap.error || "failed"));
|
|
681
|
+
if (msg.smtp && !msg.smtp.ok) parts.push("SMTP: " + (msg.smtp.error || "failed"));
|
|
682
|
+
statusEl.textContent = parts.length > 0 ? parts.join("; ") : (msg.error || "Connection failed");
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Handle email_account_add_result from server
|
|
687
|
+
export function handleEmailAddResult(msg) {
|
|
688
|
+
var statusEl = document.getElementById("email-setup-status");
|
|
689
|
+
if (!statusEl) return;
|
|
690
|
+
|
|
691
|
+
statusEl.style.display = "block";
|
|
692
|
+
if (msg.ok) {
|
|
693
|
+
statusEl.style.color = "#2ecc71";
|
|
694
|
+
statusEl.textContent = "Account added successfully!";
|
|
695
|
+
// Close modal after short delay
|
|
696
|
+
setTimeout(function () {
|
|
697
|
+
var modal = document.getElementById("email-setup-modal");
|
|
698
|
+
if (modal) modal.remove();
|
|
699
|
+
}, 800);
|
|
700
|
+
} else {
|
|
701
|
+
statusEl.style.color = "#e74c3c";
|
|
702
|
+
statusEl.textContent = msg.error || "Failed to add account.";
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Handle email_account_remove_result from server
|
|
707
|
+
export function handleEmailRemoveResult(msg) {
|
|
708
|
+
// Account removed; list will be refreshed via email_accounts_list message
|
|
709
|
+
}
|
|
710
|
+
|
|
285
711
|
function getSourceLabel(id) {
|
|
286
712
|
if (id.startsWith("term:")) {
|
|
287
713
|
var termId = parseInt(id.split(":")[1], 10);
|
|
@@ -302,12 +728,23 @@ function getSourceLabel(id) {
|
|
|
302
728
|
}
|
|
303
729
|
return "Tab " + tabId;
|
|
304
730
|
}
|
|
731
|
+
if (id.startsWith("email:")) {
|
|
732
|
+
var accId = id.split(":")[1];
|
|
733
|
+
for (var k = 0; k < emailAccountList.length; k++) {
|
|
734
|
+
if (emailAccountList[k].id === accId) {
|
|
735
|
+
var email = emailAccountList[k].email;
|
|
736
|
+
return email.length > 30 ? email.slice(0, 27) + "..." : email;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return "Email";
|
|
740
|
+
}
|
|
305
741
|
return id;
|
|
306
742
|
}
|
|
307
743
|
|
|
308
744
|
function getSourceIcon(id) {
|
|
309
745
|
if (id.startsWith("term:")) return "square-terminal";
|
|
310
746
|
if (id.startsWith("tab:")) return "globe";
|
|
747
|
+
if (id.startsWith("email:")) return "mail";
|
|
311
748
|
return "circle";
|
|
312
749
|
}
|
|
313
750
|
|
|
@@ -321,6 +758,95 @@ export function hasActiveSources() {
|
|
|
321
758
|
return activeSourceIds.size > 0;
|
|
322
759
|
}
|
|
323
760
|
|
|
761
|
+
// --- Email Defaults Modal (project-level) ---
|
|
762
|
+
|
|
763
|
+
export function handleEmailDefaults(msg) {
|
|
764
|
+
emailDefaultAccountIds = msg.accounts || [];
|
|
765
|
+
renderEmailDefaultsList();
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
export function initEmailDefaultsModal() {
|
|
769
|
+
var btn = document.getElementById("email-sidebar-btn");
|
|
770
|
+
var modal = document.getElementById("email-defaults-modal");
|
|
771
|
+
var closeBtn = document.getElementById("email-defaults-close");
|
|
772
|
+
if (!btn || !modal) return;
|
|
773
|
+
|
|
774
|
+
btn.addEventListener("click", function () {
|
|
775
|
+
modal.classList.remove("hidden");
|
|
776
|
+
// Request current defaults from server
|
|
777
|
+
if (ctx && ctx.ws && ctx.connected) {
|
|
778
|
+
ctx.ws.send(JSON.stringify({ type: "email_defaults_get" }));
|
|
779
|
+
}
|
|
780
|
+
renderEmailDefaultsList();
|
|
781
|
+
if (typeof lucide !== "undefined") lucide.createIcons();
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
if (closeBtn) {
|
|
785
|
+
closeBtn.addEventListener("click", function () {
|
|
786
|
+
modal.classList.add("hidden");
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
var backdrop = modal.querySelector(".confirm-backdrop");
|
|
791
|
+
if (backdrop) {
|
|
792
|
+
backdrop.addEventListener("click", function () {
|
|
793
|
+
modal.classList.add("hidden");
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function renderEmailDefaultsList() {
|
|
799
|
+
var listEl = document.getElementById("email-defaults-list");
|
|
800
|
+
if (!listEl) return;
|
|
801
|
+
listEl.innerHTML = "";
|
|
802
|
+
|
|
803
|
+
if (emailAccountList.length === 0) {
|
|
804
|
+
var emptyEl = document.createElement("div");
|
|
805
|
+
emptyEl.className = "mcp-empty";
|
|
806
|
+
emptyEl.innerHTML = '<p>No email accounts connected.</p>' +
|
|
807
|
+
'<button class="us-email-add-btn" type="button" style="margin:12px auto 0">+ Add Account</button>';
|
|
808
|
+
var addBtn = emptyEl.querySelector("button");
|
|
809
|
+
addBtn.addEventListener("click", function () {
|
|
810
|
+
document.getElementById("email-defaults-modal").classList.add("hidden");
|
|
811
|
+
showEmailSetupModal();
|
|
812
|
+
});
|
|
813
|
+
listEl.appendChild(emptyEl);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
for (var i = 0; i < emailAccountList.length; i++) {
|
|
818
|
+
var acc = emailAccountList[i];
|
|
819
|
+
var isOn = emailDefaultAccountIds.indexOf(acc.id) !== -1;
|
|
820
|
+
|
|
821
|
+
var row = document.createElement("label");
|
|
822
|
+
row.className = "settings-toggle-row";
|
|
823
|
+
row.innerHTML =
|
|
824
|
+
'<div>' +
|
|
825
|
+
'<span class="settings-label">' + escapeHtml(acc.email) + '</span>' +
|
|
826
|
+
'<div class="settings-hint">' + escapeHtml(acc.label || acc.provider || "Custom") + '</div>' +
|
|
827
|
+
'</div>' +
|
|
828
|
+
'<input type="checkbox" data-account-id="' + escapeHtml(acc.id) + '"' + (isOn ? ' checked' : '') + '>' +
|
|
829
|
+
'<span class="toggle-track"><span class="toggle-thumb"></span></span>';
|
|
830
|
+
|
|
831
|
+
var checkbox = row.querySelector("input");
|
|
832
|
+
checkbox.addEventListener("change", function () {
|
|
833
|
+
var accId = this.getAttribute("data-account-id");
|
|
834
|
+
var idx = emailDefaultAccountIds.indexOf(accId);
|
|
835
|
+
if (this.checked && idx === -1) {
|
|
836
|
+
emailDefaultAccountIds.push(accId);
|
|
837
|
+
} else if (!this.checked && idx !== -1) {
|
|
838
|
+
emailDefaultAccountIds.splice(idx, 1);
|
|
839
|
+
}
|
|
840
|
+
// Save to server
|
|
841
|
+
if (ctx && ctx.ws && ctx.connected) {
|
|
842
|
+
ctx.ws.send(JSON.stringify({ type: "email_defaults_save", accounts: emailDefaultAccountIds }));
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
listEl.appendChild(row);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
324
850
|
function escapeHtml(str) {
|
|
325
851
|
var div = document.createElement("div");
|
|
326
852
|
div.textContent = str;
|
|
@@ -7,6 +7,7 @@ import { renderUnifiedDiff, renderSplitDiff } from './diff.js';
|
|
|
7
7
|
import { initFileIcons, getFileIconSvg, getFolderIconSvg } from './fileicons.js';
|
|
8
8
|
|
|
9
9
|
var ctx;
|
|
10
|
+
var showDropHint = function () {};
|
|
10
11
|
var treeData = {}; // path -> { loaded, children }
|
|
11
12
|
var currentContent = null; // last read file content for copy
|
|
12
13
|
var currentFilePath = null; // path of the currently viewed file
|
|
@@ -34,6 +35,66 @@ export function initFileBrowser(_ctx) {
|
|
|
34
35
|
}
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
// --- Drag-and-drop file paths into message input ---
|
|
39
|
+
var inputEl = document.getElementById("input");
|
|
40
|
+
var dropHintEl = null;
|
|
41
|
+
var dropHintTimer = null;
|
|
42
|
+
|
|
43
|
+
showDropHint = function () {
|
|
44
|
+
if (!inputEl) return;
|
|
45
|
+
if (!dropHintEl) {
|
|
46
|
+
dropHintEl = document.createElement("div");
|
|
47
|
+
dropHintEl.className = "fb-drop-hint";
|
|
48
|
+
dropHintEl.textContent = "Drop here to insert file path";
|
|
49
|
+
inputEl.parentElement.style.position = "relative";
|
|
50
|
+
inputEl.parentElement.appendChild(dropHintEl);
|
|
51
|
+
}
|
|
52
|
+
dropHintEl.classList.add("visible");
|
|
53
|
+
clearTimeout(dropHintTimer);
|
|
54
|
+
// Auto-hide after drag ends without drop
|
|
55
|
+
dropHintTimer = setTimeout(function () { hideDropHint(); }, 3000);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function hideDropHint() {
|
|
59
|
+
clearTimeout(dropHintTimer);
|
|
60
|
+
if (dropHintEl) dropHintEl.classList.remove("visible");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (inputEl) {
|
|
64
|
+
inputEl.addEventListener("dragover", function (e) {
|
|
65
|
+
if (e.dataTransfer.types.indexOf("text/plain") !== -1) {
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
e.dataTransfer.dropEffect = "copy";
|
|
68
|
+
inputEl.classList.add("drop-target");
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
inputEl.addEventListener("dragleave", function () {
|
|
72
|
+
inputEl.classList.remove("drop-target");
|
|
73
|
+
});
|
|
74
|
+
inputEl.addEventListener("drop", function (e) {
|
|
75
|
+
inputEl.classList.remove("drop-target");
|
|
76
|
+
hideDropHint();
|
|
77
|
+
var filePath = e.dataTransfer.getData("text/plain");
|
|
78
|
+
if (!filePath) return;
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
var cursorPos = inputEl.selectionStart || 0;
|
|
81
|
+
var before = inputEl.value.substring(0, cursorPos);
|
|
82
|
+
var after = inputEl.value.substring(cursorPos);
|
|
83
|
+
var prefix = before.length > 0 && before[before.length - 1] !== " " && before[before.length - 1] !== "\n" ? " " : "";
|
|
84
|
+
var suffix = after.length > 0 && after[0] !== " " && after[0] !== "\n" ? " " : "";
|
|
85
|
+
inputEl.value = before + prefix + filePath + suffix + after;
|
|
86
|
+
var newPos = cursorPos + prefix.length + filePath.length + suffix.length;
|
|
87
|
+
inputEl.setSelectionRange(newPos, newPos);
|
|
88
|
+
inputEl.focus();
|
|
89
|
+
inputEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
90
|
+
});
|
|
91
|
+
// Hide hint when drag ends anywhere on the page
|
|
92
|
+
document.addEventListener("dragend", function () {
|
|
93
|
+
inputEl.classList.remove("drop-target");
|
|
94
|
+
hideDropHint();
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
37
98
|
// Close button
|
|
38
99
|
document.getElementById("file-viewer-close").addEventListener("click", function () {
|
|
39
100
|
closeFileViewer();
|
|
@@ -418,6 +479,17 @@ function renderEntries(container, entries, depth) {
|
|
|
418
479
|
row.className = "file-tree-item";
|
|
419
480
|
row.style.paddingLeft = (8 + depth * 16) + "px";
|
|
420
481
|
|
|
482
|
+
row.draggable = true;
|
|
483
|
+
row.dataset.path = entry.path;
|
|
484
|
+
row.addEventListener("dragstart", function (e) {
|
|
485
|
+
var cwd = ctx.cwd || "";
|
|
486
|
+
var rel = this.dataset.path;
|
|
487
|
+
var abs = cwd ? cwd.replace(/\/$/, "") + "/" + rel : rel;
|
|
488
|
+
e.dataTransfer.setData("text/plain", abs);
|
|
489
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
490
|
+
showDropHint();
|
|
491
|
+
});
|
|
492
|
+
|
|
421
493
|
if (entry.type === "dir") {
|
|
422
494
|
row.innerHTML =
|
|
423
495
|
'<span class="file-tree-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
@@ -108,6 +108,13 @@ export function initMateSidebar(wsGetter) {
|
|
|
108
108
|
if (origBtn) origBtn.click();
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
+
var mateEmailBtn = document.getElementById("mate-email-btn");
|
|
112
|
+
if (mateEmailBtn) {
|
|
113
|
+
mateEmailBtn.addEventListener("click", function () {
|
|
114
|
+
var origBtn = document.getElementById("email-sidebar-btn");
|
|
115
|
+
if (origBtn) origBtn.click();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
111
118
|
var mateSkillsBtn = document.getElementById("mate-skills-btn");
|
|
112
119
|
if (mateSkillsBtn) {
|
|
113
120
|
mateSkillsBtn.addEventListener("click", function () {
|
|
@@ -856,7 +856,7 @@ function renderSheetMateProfile(listEl) {
|
|
|
856
856
|
// Action buttons
|
|
857
857
|
var actions = [
|
|
858
858
|
{ icon: "book-open", label: "Knowledge", btnId: "mate-knowledge-btn", countId: "mate-knowledge-count" },
|
|
859
|
-
{ icon: "sticky-note", label: "Sticky Notes", btnId: "sticky-notes-
|
|
859
|
+
{ icon: "sticky-note", label: "Sticky Notes", btnId: "sticky-notes-sidebar-btn", countId: "sticky-notes-sidebar-count" },
|
|
860
860
|
{ icon: "puzzle", label: "Skills", btnId: "mate-skills-btn" },
|
|
861
861
|
{ icon: "calendar", label: "Scheduled Tasks", btnId: "mate-scheduler-btn" }
|
|
862
862
|
];
|