clay-server 3.2.2-beta.3 → 3.3.0-beta.2

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.
@@ -10,7 +10,6 @@ var emailUnreadCounts = {}; // accountId -> unread count
10
10
  var _emailTestPassed = false;
11
11
  var _emailPendingSave = false;
12
12
  var _emailDoSave = null; // set by showEmailSetupModal
13
- var emailDefaultAccountIds = []; // project-level defaults
14
13
 
15
14
  export function initContextSources(_ctx) {
16
15
  ctx = _ctx;
@@ -758,95 +757,6 @@ export function hasActiveSources() {
758
757
  return activeSourceIds.size > 0;
759
758
  }
760
759
 
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
-
850
760
  function escapeHtml(str) {
851
761
  var div = document.createElement("div");
852
762
  div.textContent = str;
@@ -7,6 +7,8 @@ import { renderUnifiedDiff, renderSplitDiff } from './diff.js';
7
7
  import { initFileIcons, getFileIconSvg, getFolderIconSvg } from './fileicons.js';
8
8
  import { copyMarkdownFormatting } from './rich-clipboard.js';
9
9
  import { animateMarkdownChange, beginMarkdownPresentation, cancelMarkdownFollow, isFollowingMarkdown } from './markdown-live-edit.js';
10
+ import { store } from './store.js';
11
+ import { enterMarkdownSlides, exitMarkdownSlides, handleMarkdownSlideKey, syncMarkdownSlidesButton, toggleMarkdownSlideLevelMenu } from './markdown-slides.js';
10
12
 
11
13
  var ctx;
12
14
  var showDropHint = function () {};
@@ -134,14 +136,26 @@ export function initFileBrowser(_ctx) {
134
136
  closeFileViewer();
135
137
  });
136
138
 
137
- // Fullscreen toggle
138
- document.getElementById("file-viewer-fullscreen").addEventListener("click", function () {
139
- var isFs = ctx.fileViewerEl.classList.toggle("panel-fullscreen");
140
- var icon = this.querySelector("[data-lucide]");
141
- if (icon) {
142
- icon.setAttribute("data-lucide", isFs ? "minimize-2" : "maximize-2");
143
- refreshIcons();
139
+ // Full-viewport presentation toggle
140
+ var fullscreenBtn = document.getElementById("file-viewer-fullscreen");
141
+ fullscreenBtn.setAttribute("aria-pressed", "false");
142
+ fullscreenBtn.addEventListener("click", function () {
143
+ if (store.get('markdownSlidesActive')) exitMarkdownSlides();
144
+ setFileViewerFullscreen(!store.get('fileViewerFullscreen'));
145
+ });
146
+
147
+ document.getElementById("file-viewer-slides").addEventListener("click", function () {
148
+ if (store.get('markdownSlidesActive')) {
149
+ exitMarkdownSlides();
150
+ return;
144
151
  }
152
+ var markdownEl = ensureRenderedMarkdown();
153
+ if (enterMarkdownSlides(markdownEl, 0)) setFileViewerFullscreen(true);
154
+ });
155
+
156
+ document.getElementById("file-viewer-slide-level").addEventListener("click", function () {
157
+ var markdownEl = markdownElementForActions();
158
+ toggleMarkdownSlideLevelMenu(markdownEl);
145
159
  });
146
160
 
147
161
  // Copy button
@@ -150,7 +164,7 @@ export function initFileBrowser(_ctx) {
150
164
  });
151
165
 
152
166
  document.getElementById("file-viewer-copy-formatted").addEventListener("click", function () {
153
- if (!isRendered || !currentIsMarkdown) return;
167
+ if (!currentIsMarkdown) return;
154
168
  copyMarkdownFormatting(currentContent).catch(function (err) {
155
169
  console.error("Markdown formatting copy failed:", err);
156
170
  });
@@ -166,8 +180,8 @@ export function initFileBrowser(_ctx) {
166
180
 
167
181
  // PDF export button
168
182
  document.getElementById("file-viewer-pdf").addEventListener("click", function () {
169
- if (!isRendered || !currentIsMarkdown) return;
170
- var mdEl = document.querySelector(".file-viewer-markdown");
183
+ if (!currentIsMarkdown) return;
184
+ var mdEl = ensureRenderedMarkdown();
171
185
  if (!mdEl) return;
172
186
  var btn = document.getElementById("file-viewer-pdf");
173
187
  btn.disabled = true;
@@ -221,7 +235,13 @@ export function initFileBrowser(_ctx) {
221
235
 
222
236
  // ESC to close
223
237
  document.addEventListener("keydown", function (e) {
238
+ if (handleMarkdownSlideKey(e)) return;
224
239
  if (e.key === "Escape" && !ctx.fileViewerEl.classList.contains("hidden")) {
240
+ if (store.get('fileViewerFullscreen')) {
241
+ e.preventDefault();
242
+ setFileViewerFullscreen(false);
243
+ return;
244
+ }
225
245
  closeFileViewer();
226
246
  }
227
247
  });
@@ -408,17 +428,48 @@ function sendUnwatch() {
408
428
  }
409
429
  }
410
430
 
431
+ function markdownElementForActions() {
432
+ var rendered = document.querySelector("#file-viewer-body .file-viewer-markdown");
433
+ if (rendered) return rendered;
434
+ if (!currentIsMarkdown || currentContent == null) return null;
435
+ var preview = document.createElement("div");
436
+ preview.className = "file-viewer-markdown";
437
+ preview.innerHTML = renderMarkdown(currentContent);
438
+ return preview;
439
+ }
440
+
441
+ function ensureRenderedMarkdown() {
442
+ if (!currentIsMarkdown || currentContent == null) return null;
443
+ if (!isRendered) {
444
+ isRendered = true;
445
+ renderBody();
446
+ }
447
+ return document.querySelector("#file-viewer-body .file-viewer-markdown");
448
+ }
449
+
411
450
  export function closeFileViewer() {
412
451
  if (currentFilePath && isFollowingMarkdown(currentFilePath)) cancelMarkdownFollow();
413
452
  sendUnwatch();
414
453
  inlineDiffActive = false;
454
+ exitMarkdownSlides();
415
455
  ctx.fileViewerEl.classList.remove("file-viewer-wide");
416
- ctx.fileViewerEl.classList.remove("panel-fullscreen");
456
+ setFileViewerFullscreen(false);
417
457
  ctx.fileViewerEl.classList.add("hidden");
418
- // Reset fullscreen icon
419
- var fsIcon = document.querySelector("#file-viewer-fullscreen [data-lucide]");
420
- if (fsIcon) {
421
- fsIcon.setAttribute("data-lucide", "maximize-2");
458
+ }
459
+
460
+ export function setFileViewerFullscreen(enabled) {
461
+ var isFullscreen = !!enabled;
462
+ var viewer = ctx && ctx.fileViewerEl ? ctx.fileViewerEl : document.getElementById("file-viewer");
463
+ var button = document.getElementById("file-viewer-fullscreen");
464
+ if (viewer) viewer.classList.toggle("panel-fullscreen", isFullscreen);
465
+ store.set({ fileViewerFullscreen: isFullscreen });
466
+ if (!button) return;
467
+ button.setAttribute("aria-pressed", isFullscreen ? "true" : "false");
468
+ button.setAttribute("aria-label", isFullscreen ? "Exit presentation mode" : "Enter presentation mode");
469
+ button.title = isFullscreen ? "Exit presentation mode (Esc)" : "Enter presentation mode";
470
+ var icon = button.querySelector("[data-lucide]");
471
+ if (icon) {
472
+ icon.setAttribute("data-lucide", isFullscreen ? "minimize-2" : "maximize-2");
422
473
  refreshIcons();
423
474
  }
424
475
  }
@@ -470,6 +521,23 @@ export function openFile(filePath, opts) {
470
521
  requestFileContent(filePath);
471
522
  }
472
523
 
524
+ export function openWorkingTreeDiff(diff) {
525
+ if (!diff || !diff.path) return;
526
+ pendingRenderedOpen = false;
527
+ pendingOpenMode = diff.binary ? null : {
528
+ type: "diff",
529
+ oldStr: diff.oldContent || "",
530
+ newStr: diff.newContent || "",
531
+ review: diff.review || null,
532
+ };
533
+ showFileContent({
534
+ path: diff.path,
535
+ content: diff.newContent || "",
536
+ size: (diff.newContent || "").length,
537
+ binary: !!diff.binary,
538
+ });
539
+ }
540
+
473
541
  export function presentMarkdownEdit(msg) {
474
542
  if (!msg || !beginMarkdownPresentation(msg.path)) return;
475
543
  pendingOpenMode = null;
@@ -486,11 +554,14 @@ function followedFileChanged(filePath) {
486
554
  return currentFilePath && isFollowingMarkdown(currentFilePath) && !isFollowingMarkdown(filePath);
487
555
  }
488
556
 
489
- function renderBody(previousMarkdown) {
557
+ function renderBody(previousMarkdown, requestedSlideIndex) {
490
558
  var bodyEl = document.getElementById("file-viewer-body");
491
559
  var renderBtn = document.getElementById("file-viewer-render");
492
560
  var pdfBtn = document.getElementById("file-viewer-pdf");
493
561
  var formattedCopyBtn = document.getElementById("file-viewer-copy-formatted");
562
+ var resumeSlides = store.get('markdownSlidesActive') || typeof requestedSlideIndex === "number";
563
+ var resumeSlideIndex = typeof requestedSlideIndex === "number" ? requestedSlideIndex : (store.get('markdownSlideIndex') || 0);
564
+ if (store.get('markdownSlidesActive')) exitMarkdownSlides();
494
565
 
495
566
  if (isRendered) {
496
567
  bodyEl.innerHTML = '<div class="file-viewer-markdown">' + renderMarkdown(currentContent) + '</div>';
@@ -510,6 +581,8 @@ function renderBody(previousMarkdown) {
510
581
  }
511
582
  highlightCodeBlocks(bodyEl);
512
583
  renderMermaidBlocks(bodyEl);
584
+ var slideCount = syncMarkdownSlidesButton(markdownEl);
585
+ if (resumeSlides && slideCount > 0) enterMarkdownSlides(markdownEl, resumeSlideIndex);
513
586
  renderBtn.classList.add("active");
514
587
  renderBtn.title = "Show raw";
515
588
  pdfBtn.classList.remove("hidden");
@@ -527,8 +600,9 @@ function renderBody(previousMarkdown) {
527
600
  }
528
601
  renderBtn.classList.remove("active");
529
602
  renderBtn.title = "Render markdown";
530
- pdfBtn.classList.add("hidden");
531
- formattedCopyBtn.classList.add("hidden");
603
+ pdfBtn.classList.remove("hidden");
604
+ formattedCopyBtn.classList.remove("hidden");
605
+ syncMarkdownSlidesButton(markdownElementForActions());
532
606
  }
533
607
  refreshIcons();
534
608
  }
@@ -964,6 +1038,11 @@ function showFileContent(msg) {
964
1038
  var previousContent = currentContent;
965
1039
  var previousPath = currentFilePath;
966
1040
  var previousWasMarkdown = currentIsMarkdown;
1041
+ var refreshSlideIndex = pendingRefresh && store.get('markdownSlidesActive')
1042
+ ? store.get('markdownSlideIndex') || 0
1043
+ : null;
1044
+
1045
+ exitMarkdownSlides();
967
1046
 
968
1047
  pathEl.textContent = msg.path;
969
1048
  var keepRenderState = pendingRefresh && msg.path === currentFilePath;
@@ -982,6 +1061,7 @@ function showFileContent(msg) {
982
1061
  renderBtn.classList.remove("active");
983
1062
  document.getElementById("file-viewer-pdf").classList.add("hidden");
984
1063
  document.getElementById("file-viewer-copy-formatted").classList.add("hidden");
1064
+ document.getElementById("file-viewer-slides").classList.add("hidden");
985
1065
 
986
1066
  if (msg.error) {
987
1067
  bodyEl.innerHTML = '<div class="file-tree-error">' + escapeHtml(msg.error) + '</div>';
@@ -1011,7 +1091,7 @@ function showFileContent(msg) {
1011
1091
  isFollowingMarkdown(msg.path) && pathsReferToSameFile(previousPath, msg.path)
1012
1092
  ? (previousContent == null ? "" : previousContent)
1013
1093
  : null;
1014
- renderBody(transitionFrom);
1094
+ renderBody(transitionFrom, refreshSlideIndex);
1015
1095
  } else {
1016
1096
  renderCodeWithLineNumbers(bodyEl, msg.content, ext);
1017
1097
  }
@@ -1036,7 +1116,7 @@ function showFileContent(msg) {
1036
1116
  historyBtn2.classList.add("hidden");
1037
1117
  historyBtn2.classList.remove("active");
1038
1118
  requestFileHistory(msg.path);
1039
- showInlineDiff(diffOpts.oldStr, diffOpts.newStr);
1119
+ showInlineDiff(diffOpts.oldStr, diffOpts.newStr, diffOpts.review);
1040
1120
  pendingRenderedOpen = false;
1041
1121
  return;
1042
1122
  }
@@ -1078,7 +1158,7 @@ export function handleFileChanged(msg) {
1078
1158
  if (bodyEl) bodyEl.scrollTop = scrollPos;
1079
1159
  }
1080
1160
 
1081
- function showInlineDiff(oldStr, newStr) {
1161
+ function showInlineDiff(oldStr, newStr, review) {
1082
1162
  var bodyEl = document.getElementById("file-viewer-body");
1083
1163
  inlineDiffActive = true;
1084
1164
  ctx.fileViewerEl.classList.add("file-viewer-wide");
@@ -1115,6 +1195,40 @@ function showInlineDiff(oldStr, newStr) {
1115
1195
  });
1116
1196
  topBar.appendChild(backBtn);
1117
1197
 
1198
+ if (review) {
1199
+ var reviewNav = document.createElement("div");
1200
+ reviewNav.className = "git-diff-review-nav";
1201
+ var reviewCount = document.createElement("span");
1202
+ reviewCount.textContent = (review.index + 1) + " of " + review.total;
1203
+ reviewNav.appendChild(reviewCount);
1204
+ var previousBtn = document.createElement("button");
1205
+ previousBtn.type = "button";
1206
+ previousBtn.textContent = "\u2190";
1207
+ previousBtn.title = "Previous changed file";
1208
+ previousBtn.disabled = !review.previous;
1209
+ if (review.previous) previousBtn.addEventListener("click", review.previous);
1210
+ reviewNav.appendChild(previousBtn);
1211
+ var nextBtn = document.createElement("button");
1212
+ nextBtn.type = "button";
1213
+ nextBtn.textContent = "\u2192";
1214
+ nextBtn.title = "Next changed file";
1215
+ nextBtn.disabled = !review.next;
1216
+ if (review.next) nextBtn.addEventListener("click", review.next);
1217
+ reviewNav.appendChild(nextBtn);
1218
+ if (review.askAgent) {
1219
+ var askBtn = document.createElement("button");
1220
+ askBtn.type = "button";
1221
+ askBtn.className = "git-diff-ask-agent";
1222
+ askBtn.innerHTML = iconHtml("sparkles") + "<span>Review with Agent</span>";
1223
+ askBtn.setAttribute("data-tip", "Start a new agent session to review this file's current Git changes");
1224
+ askBtn.setAttribute("aria-label", "Review this file's Git changes with an agent");
1225
+ askBtn.addEventListener("click", review.askAgent);
1226
+ reviewNav.appendChild(askBtn);
1227
+ refreshIcons(askBtn);
1228
+ }
1229
+ topBar.appendChild(reviewNav);
1230
+ }
1231
+
1118
1232
  var toggleWrap = document.createElement("div");
1119
1233
  toggleWrap.className = "file-history-view-toggle";
1120
1234