clay-server 2.30.0 → 2.31.0-beta.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 (36) hide show
  1. package/lib/email-accounts.js +299 -0
  2. package/lib/email-mcp-server.js +646 -0
  3. package/lib/project-connection.js +26 -2
  4. package/lib/project-email.js +418 -0
  5. package/lib/project-sessions.js +16 -0
  6. package/lib/project-user-message.js +26 -5
  7. package/lib/project.js +72 -25
  8. package/lib/public/app.js +18 -5
  9. package/lib/public/css/filebrowser.css +80 -2
  10. package/lib/public/css/input.css +196 -0
  11. package/lib/public/css/notifications-center.css +3 -0
  12. package/lib/public/css/sidebar.css +77 -2
  13. package/lib/public/css/sticky-notes.css +0 -48
  14. package/lib/public/css/user-settings.css +85 -0
  15. package/lib/public/icons/email/gmail.svg +7 -0
  16. package/lib/public/icons/email/outlook.svg +35 -0
  17. package/lib/public/icons/email/yahoo.svg +1 -0
  18. package/lib/public/index.html +36 -3
  19. package/lib/public/modules/app-dm.js +4 -9
  20. package/lib/public/modules/app-messages.js +37 -2
  21. package/lib/public/modules/app-panels.js +2 -1
  22. package/lib/public/modules/context-sources.js +527 -1
  23. package/lib/public/modules/filebrowser.js +72 -0
  24. package/lib/public/modules/mate-sidebar.js +7 -0
  25. package/lib/public/modules/sidebar-mobile.js +1 -1
  26. package/lib/public/modules/sidebar.js +144 -2
  27. package/lib/public/modules/sticky-notes.js +1 -91
  28. package/lib/public/modules/terminal.js +0 -12
  29. package/lib/public/modules/theme.js +4 -0
  30. package/lib/public/modules/tools.js +23 -0
  31. package/lib/public/modules/user-settings.js +74 -0
  32. package/lib/sdk-bridge.js +16 -0
  33. package/lib/sdk-message-processor.js +33 -0
  34. package/lib/server-email.js +148 -0
  35. package/lib/server.js +5 -0
  36. package/package.json +3 -2
package/lib/project.js CHANGED
@@ -28,15 +28,17 @@ var { attachUserMessage } = require("./project-user-message");
28
28
  var { attachConnection } = require("./project-connection");
29
29
  var { attachMcp } = require("./project-mcp");
30
30
  var { createLocalMcp } = require("./mcp-local");
31
+ var { attachEmail: attachEmailModule } = require("./project-email");
31
32
  // project-notifications is attached globally in server.js, passed via opts.notificationsModule
32
33
 
33
34
  // --- Context Sources persistence ---
34
35
  var _ctxSrcConfig = require("./config");
35
36
  var _ctxSrcDir = path.join(_ctxSrcConfig.CONFIG_DIR, "context-sources");
36
37
 
37
- function loadContextSources(slug) {
38
+ function loadContextSources(slug, sessionId) {
38
39
  try {
39
- var filePath = path.join(_ctxSrcDir, slug + ".json");
40
+ var key = sessionId ? slug + "--" + sessionId : slug;
41
+ var filePath = path.join(_ctxSrcDir, key + ".json");
40
42
  var data = JSON.parse(fs.readFileSync(filePath, "utf8"));
41
43
  return data.active || [];
42
44
  } catch (e) {
@@ -44,12 +46,13 @@ function loadContextSources(slug) {
44
46
  }
45
47
  }
46
48
 
47
- function saveContextSources(slug, activeIds) {
49
+ function saveContextSources(slug, sessionId, activeIds) {
48
50
  try {
49
51
  if (!fs.existsSync(_ctxSrcDir)) {
50
52
  fs.mkdirSync(_ctxSrcDir, { recursive: true });
51
53
  }
52
- var filePath = path.join(_ctxSrcDir, slug + ".json");
54
+ var key = sessionId ? slug + "--" + sessionId : slug;
55
+ var filePath = path.join(_ctxSrcDir, key + ".json");
53
56
  fs.writeFileSync(filePath, JSON.stringify({ active: activeIds }), "utf8");
54
57
  } catch (e) {
55
58
  console.error("[context-sources] Failed to save:", e.message);
@@ -255,15 +258,19 @@ function createProjectContext(opts) {
255
258
  return sendExtensionCommand(browserState._extensionWs, command, args, timeout);
256
259
  }
257
260
 
258
- function requestTabContext(ws, tabId) {
261
+ function requestTabContext(tabId) {
262
+ if (!browserState._extensionWs || browserState._extensionWs.readyState !== 1) {
263
+ return Promise.resolve(null);
264
+ }
265
+ var extWs = browserState._extensionWs;
259
266
  // Try inject first (best-effort), then request all data in parallel.
260
267
  // Even if inject fails (CSP etc.), page text and screenshot still work.
261
- return sendExtensionCommand(ws, "tab_inject", { tabId: tabId }).then(function() {}, function() {}).then(function() {
268
+ return sendExtensionCommand(extWs, "tab_inject", { tabId: tabId }).then(function() {}, function() {}).then(function() {
262
269
  return Promise.all([
263
- sendExtensionCommand(ws, "tab_console", { tabId: tabId }),
264
- sendExtensionCommand(ws, "tab_network", { tabId: tabId }),
265
- sendExtensionCommand(ws, "tab_page_text", { tabId: tabId }),
266
- sendExtensionCommand(ws, "tab_screenshot", { tabId: tabId })
270
+ sendExtensionCommand(extWs, "tab_console", { tabId: tabId }),
271
+ sendExtensionCommand(extWs, "tab_network", { tabId: tabId }),
272
+ sendExtensionCommand(extWs, "tab_page_text", { tabId: tabId }),
273
+ sendExtensionCommand(extWs, "tab_screenshot", { tabId: tabId })
267
274
  ]);
268
275
  }).then(function(results) {
269
276
  return {
@@ -432,6 +439,18 @@ function createProjectContext(opts) {
432
439
  localMcp: _localMcp,
433
440
  });
434
441
 
442
+ // --- Email module (delegated to project-email.js) ---
443
+ var _email = attachEmailModule({
444
+ slug: slug,
445
+ send: send,
446
+ sendTo: sendTo,
447
+ clients: clients,
448
+ loadContextSources: loadContextSources,
449
+ getUserIdForWs: function (ws) {
450
+ return (ws._clayUser && ws._clayUser.id) || "default";
451
+ },
452
+ });
453
+
435
454
  // --- SDK bridge ---
436
455
  var sdk = createSDKBridge({
437
456
  cwd: cwd,
@@ -476,24 +495,31 @@ function createProjectContext(opts) {
476
495
  }, {
477
496
  watchTab: function (tabId) {
478
497
  var key = "tab:" + tabId;
479
- var active = loadContextSources(slug);
480
- if (active.indexOf(key) === -1) {
481
- active.push(key);
482
- saveContextSources(slug, active);
483
- var _msg = JSON.stringify({ type: "context_sources_state", active: active });
484
- for (var c of clients) { if (c.readyState === 1) c.send(_msg); }
498
+ // Apply to all connected clients' active sessions
499
+ for (var c of clients) {
500
+ if (c.readyState !== 1) continue;
501
+ var sid = c._clayActiveSession || null;
502
+ var active = loadContextSources(slug, sid);
503
+ if (active.indexOf(key) === -1) {
504
+ active.push(key);
505
+ saveContextSources(slug, sid, active);
506
+ c.send(JSON.stringify({ type: "context_sources_state", active: active }));
507
+ }
485
508
  }
486
- return active;
509
+ return [];
487
510
  },
488
511
  unwatchTab: function (tabId) {
489
512
  var key = "tab:" + tabId;
490
- var active = loadContextSources(slug);
491
- var idx = active.indexOf(key);
492
- if (idx !== -1) {
493
- active.splice(idx, 1);
494
- saveContextSources(slug, active);
495
- var _msg = JSON.stringify({ type: "context_sources_state", active: active });
496
- for (var c of clients) { if (c.readyState === 1) c.send(_msg); }
513
+ for (var c of clients) {
514
+ if (c.readyState !== 1) continue;
515
+ var sid = c._clayActiveSession || null;
516
+ var active = loadContextSources(slug, sid);
517
+ var idx = active.indexOf(key);
518
+ if (idx !== -1) {
519
+ active.splice(idx, 1);
520
+ saveContextSources(slug, sid, active);
521
+ c.send(JSON.stringify({ type: "context_sources_state", active: active }));
522
+ }
497
523
  }
498
524
  return active;
499
525
  },
@@ -504,6 +530,17 @@ function createProjectContext(opts) {
504
530
  }
505
531
  }
506
532
 
533
+ // Email MCP server (available to both mates and main project)
534
+ try {
535
+ var emailMcp = require("./email-mcp-server");
536
+ // Use "default" userId initially; the actual userId is resolved per-tool-call
537
+ // via the deps closures which read from the active WS connection.
538
+ var emailMcpConfig = emailMcp.create(_email.createMcpDeps());
539
+ if (emailMcpConfig) servers[emailMcpConfig.name || "clay-email"] = emailMcpConfig;
540
+ } catch (e) {
541
+ console.error("[project] Failed to create email MCP server:", e.message);
542
+ }
543
+
507
544
  return Object.keys(servers).length > 0 ? servers : undefined;
508
545
  })(),
509
546
  getRemoteMcpServers: function () { return _mcp.getMcpServers(); },
@@ -651,7 +688,7 @@ function createProjectContext(opts) {
651
688
  }
652
689
 
653
690
  // --- DM messages (delegated to server-level handler) ---
654
- if (msg.type === "dm_open" || msg.type === "dm_send" || msg.type === "dm_list" || msg.type === "dm_typing" || msg.type === "dm_add_favorite" || msg.type === "dm_remove_favorite" || msg.type === "mate_create" || msg.type === "mate_list" || msg.type === "mate_delete" || msg.type === "mate_update" || msg.type === "mate_readd_builtin" || msg.type === "mate_list_available_builtins") {
691
+ if (msg.type === "dm_open" || msg.type === "dm_send" || msg.type === "dm_list" || msg.type === "dm_typing" || msg.type === "dm_add_favorite" || msg.type === "dm_remove_favorite" || msg.type === "mate_create" || msg.type === "mate_list" || msg.type === "mate_delete" || msg.type === "mate_update" || msg.type === "mate_readd_builtin" || msg.type === "mate_list_available_builtins" || msg.type === "email_accounts_list" || msg.type === "email_account_add" || msg.type === "email_account_remove" || msg.type === "email_account_test") {
655
692
  if (typeof opts.onDmMessage === "function") {
656
693
  opts.onDmMessage(ws, msg);
657
694
  }
@@ -733,6 +770,9 @@ function createProjectContext(opts) {
733
770
  return;
734
771
  }
735
772
 
773
+ // --- Email defaults (project-level) ---
774
+ if (_email.handleEmailMessage(ws, msg)) return;
775
+
736
776
  // --- MCP bridge (remote MCP servers via extension) ---
737
777
  if (_mcp.handleMcpMessage(ws, msg)) return;
738
778
 
@@ -929,6 +969,9 @@ function createProjectContext(opts) {
929
969
  setLatestVersion: function (v) { latestVersion = v; },
930
970
  onCreateWorktree: onCreateWorktree,
931
971
  IGNORED_DIRS: IGNORED_DIRS,
972
+ loadContextSources: loadContextSources,
973
+ saveContextSources: saveContextSources,
974
+ _email: _email,
932
975
  });
933
976
 
934
977
  // --- User message handler (delegated to project-user-message.js) ---
@@ -970,6 +1013,7 @@ function createProjectContext(opts) {
970
1013
  getSDK: getSDK,
971
1014
  getHubSchedules: getHubSchedules,
972
1015
  getProjectOwnerId: function () { return projectOwnerId; },
1016
+ _email: _email,
973
1017
  });
974
1018
 
975
1019
  // --- Filesystem handler (delegated to project-filesystem.js) ---
@@ -1041,6 +1085,8 @@ function createProjectContext(opts) {
1041
1085
  getProjectList: getProjectList,
1042
1086
  getHubSchedules: getHubSchedules,
1043
1087
  loadContextSources: loadContextSources,
1088
+ saveContextSources: saveContextSources,
1089
+ _email: _email,
1044
1090
  restoreDebateState: restoreDebateState,
1045
1091
  stopFileWatch: stopFileWatch,
1046
1092
  stopAllDirWatches: stopAllDirWatches,
@@ -1054,6 +1100,7 @@ function createProjectContext(opts) {
1054
1100
  // --- Destroy ---
1055
1101
  function destroy() {
1056
1102
  _loop.stopTimer();
1103
+ _email.destroy();
1057
1104
  stopFileWatch();
1058
1105
  stopAllDirWatches();
1059
1106
  // Abort all active sessions and clean up mention sessions
package/lib/public/app.js CHANGED
@@ -26,8 +26,8 @@ import { initInput, clearPendingImages, handleInputSync, autoResize, builtinComm
26
26
  import { initQrCode, triggerShare } from './modules/qrcode.js';
27
27
  import { initFileBrowser, loadRootDirectory, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer, resetFileBrowser } from './modules/filebrowser.js';
28
28
  import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed, sendTerminalCommand } from './modules/terminal.js';
29
- import { initContextSources, updateTerminalList, updateBrowserTabList, handleContextSourcesState, getActiveSources, hasActiveSources } from './modules/context-sources.js';
30
- import { initStickyNotes, handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, openArchive, closeArchive, isArchiveOpen, hideNotes, showNotes, isNotesVisible } from './modules/sticky-notes.js';
29
+ import { initContextSources, initEmailDefaultsModal, updateTerminalList, updateBrowserTabList, handleContextSourcesState, getActiveSources, hasActiveSources } from './modules/context-sources.js';
30
+ import { initStickyNotes, handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, openArchive, closeArchive, isArchiveOpen, hideNotes, showNotes, isNotesVisible, createNote } from './modules/sticky-notes.js';
31
31
  import { initTheme, getThemeColor, getComputedVar, onThemeChange, getCurrentTheme, getChatLayout } from './modules/theme.js';
32
32
  import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderElicitationRequest, markElicitationResolved, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, resetTurnMetaCost, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, updateSubagentProgress, initSubagentStop, closeToolGroup, removeToolFromGroup } from './modules/tools.js';
33
33
  import { initServerSettings, updateSettingsStats, updateSettingsModels, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './modules/server-settings.js';
@@ -264,6 +264,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
264
264
  historyTotal: 0,
265
265
  replayingHistory: false,
266
266
  projectName: projectName,
267
+ cwd: "",
267
268
  currentSlug: currentSlug,
268
269
  currentProjectOwnerId: null,
269
270
  isOsUsers: false,
@@ -850,8 +851,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
850
851
  // Apply RBAC UI gating
851
852
  if (myPermissions) {
852
853
  if (!myPermissions.terminal) {
853
- var termBtn = document.getElementById("terminal-toggle-btn");
854
- if (termBtn) termBtn.style.display = "none";
855
854
  var termSideBtn = document.getElementById("terminal-sidebar-btn");
856
855
  if (termSideBtn) termSideBtn.style.display = "none";
857
856
  }
@@ -941,6 +940,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
941
940
  get ws() { return ws; },
942
941
  get connected() { return store.getState().connected; },
943
942
  get activeSessionId() { return store.getState().activeSessionId; },
943
+ get cwd() { return store.getState().cwd; },
944
944
  messagesEl: messagesEl,
945
945
  fileTreeEl: $("file-tree"),
946
946
  fileViewerEl: $("file-viewer"),
@@ -1002,10 +1002,22 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1002
1002
  get connected() { return store.getState().connected; },
1003
1003
  });
1004
1004
 
1005
- // --- Sticky Notes sidebar button (archive view) ---
1005
+ // --- Sticky Notes sidebar button (create new note) ---
1006
1006
  var stickyNotesSidebarBtn = $("sticky-notes-sidebar-btn");
1007
1007
  if (stickyNotesSidebarBtn) {
1008
1008
  stickyNotesSidebarBtn.addEventListener("click", function () {
1009
+ if (isSchedulerOpen()) closeScheduler();
1010
+ if (isArchiveOpen()) closeArchive();
1011
+ showNotes();
1012
+ createNote();
1013
+ });
1014
+ }
1015
+
1016
+ // Sticky Notes badge click → archive toggle
1017
+ var stickyNotesBadge = $("sticky-notes-sidebar-count");
1018
+ if (stickyNotesBadge) {
1019
+ stickyNotesBadge.addEventListener("click", function (e) {
1020
+ e.stopPropagation();
1009
1021
  if (isSchedulerOpen()) closeScheduler();
1010
1022
  if (isArchiveOpen()) {
1011
1023
  closeArchive();
@@ -1084,6 +1096,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1084
1096
 
1085
1097
  // --- MCP Servers ---
1086
1098
  initMcp();
1099
+ initEmailDefaultsModal();
1087
1100
 
1088
1101
  // --- Skills ---
1089
1102
  initSkills({
@@ -31,8 +31,8 @@
31
31
  #session-actions button.active { background: var(--sidebar-hover); color: var(--text); }
32
32
 
33
33
  .sidebar-badge {
34
- background: var(--accent);
35
- color: #fff;
34
+ background: rgba(var(--overlay-rgb), 0.1);
35
+ color: var(--text-dimmer);
36
36
  font-size: 10px;
37
37
  font-weight: 700;
38
38
  min-width: 16px;
@@ -47,6 +47,32 @@
47
47
  }
48
48
  .sidebar-badge.hidden { display: none; }
49
49
 
50
+ #sticky-notes-sidebar-count {
51
+ cursor: pointer;
52
+ overflow: hidden;
53
+ transition: padding 0.2s ease, background 0.15s ease, color 0.15s ease;
54
+ }
55
+ #sticky-notes-sidebar-count::after {
56
+ content: "Open Archive";
57
+ display: inline-block;
58
+ max-width: 0;
59
+ overflow: hidden;
60
+ white-space: nowrap;
61
+ vertical-align: middle;
62
+ margin-left: 0;
63
+ opacity: 0;
64
+ transition: max-width 0.2s ease, margin-left 0.2s ease, opacity 0.15s ease;
65
+ }
66
+ #sticky-notes-sidebar-count:hover {
67
+ background: var(--accent);
68
+ color: #fff;
69
+ }
70
+ #sticky-notes-sidebar-count:hover::after {
71
+ max-width: 80px;
72
+ margin-left: 4px;
73
+ opacity: 1;
74
+ }
75
+
50
76
  /* --- Panels --- */
51
77
  .sidebar-panel { flex: 1; overflow-y: auto; min-height: 0; }
52
78
  .sidebar-panel.hidden { display: none; }
@@ -61,6 +87,58 @@
61
87
  #file-viewer-refresh.spinning { animation: spin-once 0.5s ease; }
62
88
  @keyframes spin-once { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
63
89
 
90
+ /* --- File browser panel background --- */
91
+ #sidebar-panel-files {
92
+ background: var(--filebrowser-bg);
93
+ border: 1px solid var(--filebrowser-border);
94
+ border-radius: 8px;
95
+ margin: 0 6px 12px;
96
+ }
97
+
98
+ #sidebar-panel-files.fb-enter { animation: fb-in 0.2s ease-out both; }
99
+ #sidebar-panel-files.fb-exit { animation: fb-out 0.15s ease-in both; }
100
+
101
+ @keyframes fb-in {
102
+ from { opacity: 0; transform: translateY(6px); }
103
+ to { opacity: 1; transform: translateY(0); }
104
+ }
105
+ @keyframes fb-out {
106
+ from { opacity: 1; transform: translateY(0); }
107
+ to { opacity: 0; transform: translateY(6px); }
108
+ }
109
+
110
+ /* --- Drag-and-drop file path into input --- */
111
+ .file-tree-item[draggable="true"] { cursor: grab; }
112
+ .file-tree-item[draggable="true"]:active { cursor: grabbing; }
113
+
114
+ #input.drop-target {
115
+ outline: 2px solid var(--accent);
116
+ outline-offset: -2px;
117
+ background: var(--accent-8);
118
+ }
119
+
120
+ .fb-drop-hint {
121
+ position: absolute;
122
+ top: -32px;
123
+ left: 50%;
124
+ transform: translateX(-50%) translateY(4px);
125
+ background: var(--accent);
126
+ color: var(--bg);
127
+ font-size: 11px;
128
+ font-weight: 600;
129
+ padding: 5px 12px;
130
+ border-radius: 6px;
131
+ white-space: nowrap;
132
+ pointer-events: none;
133
+ opacity: 0;
134
+ box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.25);
135
+ transition: opacity 0.15s, transform 0.15s;
136
+ }
137
+ .fb-drop-hint.visible {
138
+ opacity: 1;
139
+ transform: translateX(-50%) translateY(0);
140
+ }
141
+
64
142
  /* --- File tree --- */
65
143
  #file-tree { padding: 4px 8px; }
66
144
 
@@ -479,6 +479,202 @@
479
479
  object-fit: contain;
480
480
  }
481
481
 
482
+ .context-picker-unread-badge {
483
+ margin-left: auto;
484
+ margin-right: 4px;
485
+ font-size: 11px;
486
+ font-weight: 600;
487
+ background: var(--accent, #DA7756);
488
+ color: #fff;
489
+ border-radius: 8px;
490
+ padding: 1px 6px;
491
+ min-width: 16px;
492
+ text-align: center;
493
+ line-height: 1.3;
494
+ }
495
+
496
+ .context-picker-add-item {
497
+ color: var(--text-dimmer);
498
+ font-size: 13px;
499
+ opacity: 0.8;
500
+ }
501
+ .context-picker-add-item:hover {
502
+ opacity: 1;
503
+ }
504
+
505
+ /* --- Email setup modal --- */
506
+ #email-defaults-modal { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; }
507
+ #email-defaults-modal.hidden { display: none; }
508
+
509
+ .email-setup-overlay {
510
+ position: fixed;
511
+ inset: 0;
512
+ background: rgba(0, 0, 0, 0.45);
513
+ z-index: 10000;
514
+ display: flex;
515
+ align-items: center;
516
+ justify-content: center;
517
+ }
518
+ .email-setup-dialog {
519
+ background: var(--bg);
520
+ border: 1px solid var(--border);
521
+ border-radius: 12px;
522
+ padding: 24px;
523
+ width: 420px;
524
+ max-width: 90vw;
525
+ max-height: 90vh;
526
+ overflow-y: auto;
527
+ color: var(--text);
528
+ }
529
+ .email-setup-title {
530
+ margin: 0 0 16px;
531
+ font-size: 16px;
532
+ font-weight: 600;
533
+ color: var(--text);
534
+ }
535
+ .email-setup-field {
536
+ margin-bottom: 12px;
537
+ }
538
+ .email-setup-label {
539
+ display: block;
540
+ font-size: 13px;
541
+ margin-bottom: 4px;
542
+ color: var(--text-muted);
543
+ }
544
+ .email-setup-input {
545
+ width: 100%;
546
+ padding: 8px;
547
+ border-radius: 6px;
548
+ border: 1px solid var(--border);
549
+ background: var(--input-bg);
550
+ color: var(--text);
551
+ font-size: 14px;
552
+ box-sizing: border-box;
553
+ font-family: inherit;
554
+ }
555
+ .email-setup-input:focus {
556
+ outline: none;
557
+ border-color: var(--accent);
558
+ }
559
+ .email-setup-port {
560
+ width: 70px;
561
+ flex: none !important;
562
+ }
563
+ .email-setup-row {
564
+ display: flex;
565
+ gap: 8px;
566
+ }
567
+ .email-setup-email-wrap {
568
+ display: flex;
569
+ align-items: center;
570
+ border-radius: 6px;
571
+ border: 1px solid var(--border);
572
+ background: var(--input-bg);
573
+ overflow: hidden;
574
+ }
575
+ .email-setup-email-wrap .email-setup-input {
576
+ border: none;
577
+ background: transparent;
578
+ border-radius: 0;
579
+ flex: 1;
580
+ min-width: 0;
581
+ }
582
+ .email-setup-email-wrap .email-setup-input:focus {
583
+ outline: none;
584
+ border: none;
585
+ }
586
+ .email-setup-email-wrap:focus-within {
587
+ border-color: var(--accent);
588
+ }
589
+ .email-setup-domain {
590
+ padding: 8px 10px 8px 0;
591
+ font-size: 14px;
592
+ color: var(--text-dimmer);
593
+ white-space: nowrap;
594
+ user-select: none;
595
+ pointer-events: none;
596
+ }
597
+ .email-setup-password-wrap {
598
+ position: relative;
599
+ }
600
+ .email-setup-password-wrap .email-setup-input {
601
+ padding-right: 36px;
602
+ }
603
+ .email-setup-password-eye {
604
+ position: absolute;
605
+ right: 6px;
606
+ top: 50%;
607
+ transform: translateY(-50%);
608
+ background: none;
609
+ border: none;
610
+ color: var(--text-dimmer);
611
+ cursor: pointer;
612
+ padding: 4px;
613
+ border-radius: 4px;
614
+ display: flex;
615
+ align-items: center;
616
+ justify-content: center;
617
+ }
618
+ .email-setup-password-eye:hover {
619
+ color: var(--text);
620
+ }
621
+ .email-setup-password-eye .lucide {
622
+ width: 15px;
623
+ height: 15px;
624
+ }
625
+ .email-setup-help {
626
+ font-size: 12px;
627
+ color: var(--accent);
628
+ display: inline-block;
629
+ margin-top: 4px;
630
+ }
631
+ .email-setup-help:hover {
632
+ text-decoration: underline;
633
+ }
634
+ .email-setup-status {
635
+ margin-bottom: 12px;
636
+ font-size: 13px;
637
+ display: none;
638
+ }
639
+ .email-setup-actions {
640
+ display: flex;
641
+ gap: 8px;
642
+ justify-content: flex-end;
643
+ }
644
+ .email-setup-btn {
645
+ padding: 8px 16px;
646
+ border-radius: 6px;
647
+ cursor: pointer;
648
+ font-size: 13px;
649
+ font-family: inherit;
650
+ }
651
+ .email-setup-btn-primary {
652
+ border: none;
653
+ background: var(--accent);
654
+ color: #fff;
655
+ font-weight: 600;
656
+ }
657
+ .email-setup-btn-primary:hover {
658
+ background: var(--accent-hover);
659
+ }
660
+ .email-setup-btn-secondary {
661
+ border: 1px solid var(--border);
662
+ background: var(--bg-alt);
663
+ color: var(--text);
664
+ }
665
+ .email-setup-btn-secondary:hover {
666
+ background: var(--input-bg);
667
+ }
668
+ .email-setup-btn-ghost {
669
+ border: 1px solid var(--border);
670
+ background: transparent;
671
+ color: var(--text-muted);
672
+ }
673
+ .email-setup-btn-ghost:hover {
674
+ color: var(--text);
675
+ background: rgba(var(--overlay-rgb), 0.05);
676
+ }
677
+
482
678
  /* ==========================================================================
483
679
  Input Area — Claude-style unified container
484
680
  ========================================================================== */
@@ -99,6 +99,9 @@
99
99
  font-size: 13px;
100
100
  color: var(--text);
101
101
  line-height: 1.3;
102
+ overflow: hidden;
103
+ text-overflow: ellipsis;
104
+ white-space: nowrap;
102
105
  }
103
106
  .notif-banner-text {
104
107
  font-size: 12px;
@@ -271,9 +271,53 @@
271
271
  #sidebar-tools {
272
272
  flex-shrink: 0;
273
273
  border-bottom: 1px solid var(--border-subtle);
274
- padding-top: 8px;
275
274
  padding-bottom: 4px;
276
275
  }
276
+ #sidebar-tools-toggle {
277
+ display: flex;
278
+ align-items: center;
279
+ justify-content: space-between;
280
+ width: 100%;
281
+ margin: 0;
282
+ padding: 8px 8px 4px 0;
283
+ box-sizing: border-box;
284
+ background: none;
285
+ border: none;
286
+ cursor: pointer;
287
+ font-size: 11px;
288
+ font-weight: 600;
289
+ color: var(--text-dimmer);
290
+ text-transform: uppercase;
291
+ letter-spacing: 0.5px;
292
+ font-family: inherit;
293
+ }
294
+ #sidebar-tools-toggle:hover {
295
+ color: var(--text-muted);
296
+ }
297
+ .sidebar-tools-chevron {
298
+ width: 14px;
299
+ height: 14px;
300
+ transition: transform 0.15s;
301
+ }
302
+ #sidebar-tools #session-actions {
303
+ max-height: 300px;
304
+ overflow: hidden;
305
+ transition: max-height 0.2s ease, opacity 0.2s ease;
306
+ opacity: 1;
307
+ }
308
+ #sidebar-tools.collapsed .sidebar-tools-chevron {
309
+ transform: rotate(-90deg);
310
+ }
311
+ #sidebar-tools.collapsed #session-actions {
312
+ max-height: 0;
313
+ opacity: 0;
314
+ padding-bottom: 0;
315
+ }
316
+ .sidebar-tools-divider {
317
+ height: 1px;
318
+ background: var(--border-subtle);
319
+ margin: 4px 12px;
320
+ }
277
321
 
278
322
  /* --- Sessions header (pinned above scroll) --- */
279
323
  #sidebar-sessions-header {
@@ -1332,8 +1376,39 @@
1332
1376
  display: none;
1333
1377
  }
1334
1378
 
1379
+ /* Mate sidebar resize handle (same style) */
1380
+ #mate-sidebar-resize-handle {
1381
+ position: absolute;
1382
+ top: 0;
1383
+ bottom: 0;
1384
+ width: 8px;
1385
+ margin-left: -4px;
1386
+ cursor: col-resize;
1387
+ z-index: 10;
1388
+ }
1389
+ #mate-sidebar-resize-handle::before {
1390
+ content: "";
1391
+ position: absolute;
1392
+ top: 0;
1393
+ bottom: 0;
1394
+ left: 50%;
1395
+ width: 2px;
1396
+ margin-left: -1px;
1397
+ background: var(--accent);
1398
+ opacity: 0;
1399
+ transition: opacity 0.2s;
1400
+ }
1401
+ #mate-sidebar-resize-handle:hover::before,
1402
+ #mate-sidebar-resize-handle.dragging::before {
1403
+ opacity: 1;
1404
+ }
1405
+ #mate-sidebar-resize-handle.hidden {
1406
+ display: none;
1407
+ }
1408
+
1335
1409
  @media (max-width: 768px) {
1336
- #sidebar-resize-handle {
1410
+ #sidebar-resize-handle,
1411
+ #mate-sidebar-resize-handle {
1337
1412
  display: none;
1338
1413
  }
1339
1414