clay-server 2.26.0-beta.1 → 2.26.0-beta.10

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 (42) hide show
  1. package/bin/cli.js +5 -9
  2. package/lib/browser-mcp-server.js +496 -0
  3. package/lib/daemon.js +1 -1
  4. package/lib/os-users.js +23 -0
  5. package/lib/project-debate.js +206 -88
  6. package/lib/project-mate-interaction.js +766 -0
  7. package/lib/project-memory.js +677 -0
  8. package/lib/project.js +546 -1361
  9. package/lib/public/app.js +817 -175
  10. package/lib/public/css/debate.css +224 -2
  11. package/lib/public/css/icon-strip.css +10 -10
  12. package/lib/public/css/input.css +263 -83
  13. package/lib/public/css/mates.css +56 -57
  14. package/lib/public/css/mention.css +7 -4
  15. package/lib/public/css/menus.css +7 -0
  16. package/lib/public/css/messages.css +17 -0
  17. package/lib/public/css/mobile-nav.css +3 -1
  18. package/lib/public/css/overlays.css +181 -0
  19. package/lib/public/css/rewind.css +79 -0
  20. package/lib/public/css/server-settings.css +1 -0
  21. package/lib/public/css/sidebar.css +10 -0
  22. package/lib/public/css/title-bar.css +189 -3
  23. package/lib/public/index.html +53 -16
  24. package/lib/public/modules/context-sources.js +313 -0
  25. package/lib/public/modules/debate.js +184 -97
  26. package/lib/public/modules/input.js +18 -1
  27. package/lib/public/modules/mate-knowledge.js +11 -11
  28. package/lib/public/modules/mate-memory.js +5 -5
  29. package/lib/public/modules/mate-sidebar.js +13 -9
  30. package/lib/public/modules/mention.js +40 -2
  31. package/lib/public/modules/notifications.js +109 -1
  32. package/lib/public/modules/rewind.js +36 -0
  33. package/lib/public/modules/sidebar.js +107 -28
  34. package/lib/public/modules/terminal.js +8 -0
  35. package/lib/public/modules/theme.js +2 -1
  36. package/lib/public/modules/tools.js +69 -24
  37. package/lib/sdk-bridge.js +81 -7
  38. package/lib/sdk-worker.js +13 -1
  39. package/lib/server.js +42 -0
  40. package/lib/sessions.js +39 -7
  41. package/lib/terminal-manager.js +36 -6
  42. package/package.json +2 -2
@@ -0,0 +1,313 @@
1
+ // Context Sources — attach terminal output and browser tabs as context for Claude
2
+
3
+ var ctx = null;
4
+ var activeSourceIds = new Set();
5
+ var terminalList = []; // synced from terminal module's term_list
6
+ var browserTabList = []; // synced from Chrome extension via postMessage
7
+
8
+ export function initContextSources(_ctx) {
9
+ ctx = _ctx;
10
+
11
+ var addBtn = document.getElementById("context-sources-add");
12
+ var picker = document.getElementById("context-sources-picker");
13
+
14
+ addBtn.addEventListener("click", function(e) {
15
+ e.stopPropagation();
16
+ if (picker.classList.contains("hidden")) {
17
+ renderPicker();
18
+ picker.classList.remove("hidden");
19
+ document.addEventListener("click", closePicker, true);
20
+ } else {
21
+ closePicker();
22
+ }
23
+ });
24
+
25
+ picker.addEventListener("click", function(e) {
26
+ e.stopPropagation();
27
+ });
28
+ }
29
+
30
+ function closePicker() {
31
+ var picker = document.getElementById("context-sources-picker");
32
+ picker.classList.add("hidden");
33
+ document.removeEventListener("click", closePicker, true);
34
+ }
35
+
36
+ // Restore state from server
37
+ export function handleContextSourcesState(msg) {
38
+ var saved = msg.active || [];
39
+ activeSourceIds = new Set(saved);
40
+ renderChips();
41
+ }
42
+
43
+ // Save active sources to server
44
+ function saveToServer() {
45
+ if (ctx && ctx.ws && ctx.connected) {
46
+ ctx.ws.send(JSON.stringify({
47
+ type: "context_sources_save",
48
+ active: Array.from(activeSourceIds)
49
+ }));
50
+ }
51
+ }
52
+
53
+ // Called when term_list arrives from server
54
+ export function updateTerminalList(terminals) {
55
+ terminalList = terminals || [];
56
+
57
+ // Remove active sources that no longer exist
58
+ var changed = false;
59
+ for (var id of activeSourceIds) {
60
+ if (id.startsWith("term:")) {
61
+ var termId = parseInt(id.split(":")[1], 10);
62
+ var found = false;
63
+ for (var i = 0; i < terminalList.length; i++) {
64
+ if (terminalList[i].id === termId) { found = true; break; }
65
+ }
66
+ if (!found) {
67
+ activeSourceIds.delete(id);
68
+ changed = true;
69
+ }
70
+ }
71
+ }
72
+
73
+ if (changed) saveToServer();
74
+ renderChips();
75
+
76
+ // If picker is open, re-render it
77
+ var picker = document.getElementById("context-sources-picker");
78
+ if (!picker.classList.contains("hidden")) {
79
+ renderPicker();
80
+ }
81
+ }
82
+
83
+ // Called when Chrome extension sends tab list via postMessage
84
+ export function updateBrowserTabList(tabs) {
85
+ browserTabList = tabs || [];
86
+
87
+ // Remove active tab sources that no longer exist
88
+ var changed = false;
89
+ for (var id of activeSourceIds) {
90
+ if (id.startsWith("tab:")) {
91
+ var tabId = parseInt(id.split(":")[1], 10);
92
+ var found = false;
93
+ for (var i = 0; i < browserTabList.length; i++) {
94
+ if (browserTabList[i].id === tabId) { found = true; break; }
95
+ }
96
+ if (!found) {
97
+ activeSourceIds.delete(id);
98
+ changed = true;
99
+ }
100
+ }
101
+ }
102
+
103
+ if (changed) saveToServer();
104
+ renderChips();
105
+
106
+ // If picker is open, re-render it
107
+ var picker = document.getElementById("context-sources-picker");
108
+ if (!picker.classList.contains("hidden")) {
109
+ renderPicker();
110
+ }
111
+ }
112
+
113
+ function toggleSource(sourceId) {
114
+ if (activeSourceIds.has(sourceId)) {
115
+ activeSourceIds.delete(sourceId);
116
+ } else {
117
+ activeSourceIds.add(sourceId);
118
+ }
119
+ saveToServer();
120
+ renderChips();
121
+ renderPicker();
122
+ }
123
+
124
+ function removeSource(sourceId) {
125
+ activeSourceIds.delete(sourceId);
126
+ saveToServer();
127
+ renderChips();
128
+
129
+ var picker = document.getElementById("context-sources-picker");
130
+ if (!picker.classList.contains("hidden")) {
131
+ renderPicker();
132
+ }
133
+ }
134
+
135
+ function renderChips() {
136
+ var container = document.getElementById("context-sources-chips");
137
+ container.innerHTML = "";
138
+
139
+ for (var id of activeSourceIds) {
140
+ var chip = document.createElement("div");
141
+ chip.className = "context-chip";
142
+
143
+ var label = getSourceLabel(id);
144
+ var iconName = getSourceIcon(id);
145
+
146
+ var labelEl = document.createElement("span");
147
+ labelEl.className = "context-chip-label";
148
+ labelEl.innerHTML =
149
+ '<i data-lucide="' + iconName + '"></i>' +
150
+ '<span>' + escapeHtml(label) + '</span>';
151
+ chip.appendChild(labelEl);
152
+
153
+ var removeBtn = document.createElement("button");
154
+ removeBtn.type = "button";
155
+ removeBtn.className = "context-chip-remove";
156
+ removeBtn.title = "Remove";
157
+ removeBtn.innerHTML = '<i data-lucide="minus"></i>';
158
+ removeBtn.setAttribute("data-source-id", id);
159
+ removeBtn.addEventListener("click", function(e) {
160
+ e.stopPropagation();
161
+ removeSource(this.getAttribute("data-source-id"));
162
+ if (typeof lucide !== "undefined") lucide.createIcons();
163
+ });
164
+
165
+ chip.appendChild(removeBtn);
166
+ container.appendChild(chip);
167
+ }
168
+
169
+ // Update add button label
170
+ var addBtn = document.getElementById("context-sources-add");
171
+ var labelSpan = addBtn.querySelector("span");
172
+ if (activeSourceIds.size > 0) {
173
+ labelSpan.textContent = "";
174
+ labelSpan.style.display = "none";
175
+ } else {
176
+ labelSpan.textContent = "Context Sources";
177
+ labelSpan.style.display = "";
178
+ }
179
+
180
+ if (typeof lucide !== "undefined") lucide.createIcons();
181
+ }
182
+
183
+ function renderPicker() {
184
+ // --- Terminals section ---
185
+ var termSection = document.getElementById("context-picker-terminals");
186
+ termSection.innerHTML = "";
187
+
188
+ var termLabel = document.createElement("div");
189
+ termLabel.className = "context-picker-section-label";
190
+ termLabel.textContent = "Terminals";
191
+ termSection.appendChild(termLabel);
192
+
193
+ if (terminalList.length === 0) {
194
+ var termEmpty = document.createElement("div");
195
+ termEmpty.className = "context-picker-empty";
196
+ termEmpty.textContent = "No terminals open";
197
+ termSection.appendChild(termEmpty);
198
+ } else {
199
+ for (var i = 0; i < terminalList.length; i++) {
200
+ var term = terminalList[i];
201
+ var termSourceId = "term:" + term.id;
202
+ var termActive = activeSourceIds.has(termSourceId);
203
+
204
+ var termItem = document.createElement("div");
205
+ termItem.className = "context-picker-item" + (termActive ? " active" : "");
206
+ termItem.setAttribute("data-source-id", termSourceId);
207
+
208
+ termItem.innerHTML =
209
+ '<i data-lucide="square-terminal"></i>' +
210
+ '<span>' + escapeHtml(term.title || ("Terminal " + term.id)) + '</span>' +
211
+ '<i data-lucide="check" class="context-picker-check"></i>';
212
+
213
+ termItem.addEventListener("click", function() {
214
+ toggleSource(this.getAttribute("data-source-id"));
215
+ if (typeof lucide !== "undefined") lucide.createIcons();
216
+ });
217
+
218
+ termSection.appendChild(termItem);
219
+ }
220
+ }
221
+
222
+ // --- Browser Tabs section ---
223
+ var tabSection = document.getElementById("context-picker-tabs");
224
+ tabSection.innerHTML = "";
225
+
226
+ if (browserTabList.length > 0) {
227
+ var tabLabel = document.createElement("div");
228
+ tabLabel.className = "context-picker-section-label";
229
+ tabLabel.textContent = "Browser Tabs";
230
+ tabSection.appendChild(tabLabel);
231
+
232
+ for (var j = 0; j < browserTabList.length; j++) {
233
+ var tab = browserTabList[j];
234
+ var tabSourceId = "tab:" + tab.id;
235
+ var tabActive = activeSourceIds.has(tabSourceId);
236
+
237
+ var tabItem = document.createElement("div");
238
+ tabItem.className = "context-picker-item" + (tabActive ? " active" : "");
239
+ tabItem.setAttribute("data-source-id", tabSourceId);
240
+
241
+ var tabTitle = tab.title || tab.url || "Tab";
242
+ // Truncate long URLs for display
243
+ var tabDisplay = tabTitle.length > 50 ? tabTitle.slice(0, 47) + "..." : tabTitle;
244
+
245
+ var faviconHtml = "";
246
+ if (tab.favIconUrl) {
247
+ faviconHtml = '<img src="' + escapeHtml(tab.favIconUrl) + '" class="context-picker-favicon" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'\'">' +
248
+ '<i data-lucide="globe" style="display:none"></i>';
249
+ } else {
250
+ faviconHtml = '<i data-lucide="globe"></i>';
251
+ }
252
+
253
+ tabItem.innerHTML =
254
+ faviconHtml +
255
+ '<span title="' + escapeHtml(tab.url || "") + '">' + escapeHtml(tabDisplay) + '</span>' +
256
+ '<i data-lucide="check" class="context-picker-check"></i>';
257
+
258
+ tabItem.addEventListener("click", function() {
259
+ toggleSource(this.getAttribute("data-source-id"));
260
+ if (typeof lucide !== "undefined") lucide.createIcons();
261
+ });
262
+
263
+ tabSection.appendChild(tabItem);
264
+ }
265
+ }
266
+
267
+ if (typeof lucide !== "undefined") lucide.createIcons();
268
+ }
269
+
270
+ function getSourceLabel(id) {
271
+ if (id.startsWith("term:")) {
272
+ var termId = parseInt(id.split(":")[1], 10);
273
+ for (var i = 0; i < terminalList.length; i++) {
274
+ if (terminalList[i].id === termId) {
275
+ return terminalList[i].title || ("Terminal " + termId);
276
+ }
277
+ }
278
+ return "Terminal " + termId;
279
+ }
280
+ if (id.startsWith("tab:")) {
281
+ var tabId = parseInt(id.split(":")[1], 10);
282
+ for (var j = 0; j < browserTabList.length; j++) {
283
+ if (browserTabList[j].id === tabId) {
284
+ var title = browserTabList[j].title || browserTabList[j].url || "";
285
+ return title.length > 30 ? title.slice(0, 27) + "..." : title;
286
+ }
287
+ }
288
+ return "Tab " + tabId;
289
+ }
290
+ return id;
291
+ }
292
+
293
+ function getSourceIcon(id) {
294
+ if (id.startsWith("term:")) return "square-terminal";
295
+ if (id.startsWith("tab:")) return "globe";
296
+ return "circle";
297
+ }
298
+
299
+ // Get active source IDs (for use when sending messages)
300
+ export function getActiveSources() {
301
+ return Array.from(activeSourceIds);
302
+ }
303
+
304
+ // Check if any sources are active
305
+ export function hasActiveSources() {
306
+ return activeSourceIds.size > 0;
307
+ }
308
+
309
+ function escapeHtml(str) {
310
+ var div = document.createElement("div");
311
+ div.textContent = str;
312
+ return div.innerHTML;
313
+ }
@@ -360,8 +360,9 @@ export function handleDebateEnded(msg) {
360
360
  currentTurnEl = null;
361
361
  currentTurnMateId = null;
362
362
 
363
- // Hide float info panel
363
+ // Hide float info panel and PDF button
364
364
  hideDebateInfoFloat();
365
+ showPdfBtn(false);
365
366
 
366
367
  // Ensure debate bottom bar is removed
367
368
  var bottomBar = document.getElementById("debate-bottom-bar");
@@ -369,10 +370,23 @@ export function handleDebateEnded(msg) {
369
370
  var handBar = document.getElementById("debate-hand-raise-bar");
370
371
  if (handBar) handBar.remove();
371
372
 
373
+ // Render status line in messages area
372
374
  if (ctx.messagesEl) {
373
- renderEndedBanner(msg);
375
+ var existing = ctx.messagesEl.querySelector(".debate-ended-status-line");
376
+ if (existing) existing.remove();
377
+ var statusLine = document.createElement("div");
378
+ statusLine.className = "debate-ended-status-line";
379
+ var reasonText = msg.reason === "natural" ? "Debate concluded" :
380
+ msg.reason === "user_stopped" ? "Debate stopped by user" :
381
+ "Debate ended due to error";
382
+ statusLine.innerHTML = iconHtml("check-circle") + " " + escapeHtml(reasonText) + " (" + (msg.rounds || 0) + " rounds)";
383
+ ctx.messagesEl.appendChild(statusLine);
384
+ refreshIcons();
374
385
  }
375
386
 
387
+ // Show ended mode: native input area with banner
388
+ if (ctx.showDebateEndedMode) ctx.showDebateEndedMode(msg);
389
+
376
390
  if (ctx.scrollToBottom) ctx.scrollToBottom();
377
391
  }
378
392
 
@@ -445,7 +459,7 @@ function renderEndedBanner(entry) {
445
459
  refreshIcons();
446
460
  }
447
461
 
448
- function exportDebateAsPdf() {
462
+ export function exportDebateAsPdf() {
449
463
  var popup = window.open("", "_blank", "width=900,height=700");
450
464
  if (!popup) {
451
465
  return Promise.resolve();
@@ -656,6 +670,7 @@ export function renderDebateEnded(entry) {
656
670
  debateActive = false;
657
671
  debatePhase = "ended";
658
672
  hideDebateInfoFloat();
673
+ showPdfBtn(false);
659
674
 
660
675
  // Ensure debate bars are removed during history replay
661
676
  var bottomBar = document.getElementById("debate-bottom-bar");
@@ -663,7 +678,20 @@ export function renderDebateEnded(entry) {
663
678
  var handBar = document.getElementById("debate-hand-raise-bar");
664
679
  if (handBar) handBar.remove();
665
680
 
666
- renderEndedBanner(entry);
681
+ // Render status line
682
+ var existing = ctx.messagesEl.querySelector(".debate-ended-status-line");
683
+ if (existing) existing.remove();
684
+ var statusLine = document.createElement("div");
685
+ statusLine.className = "debate-ended-status-line";
686
+ var reasonText = entry.reason === "natural" ? "Debate concluded" :
687
+ entry.reason === "user_stopped" ? "Debate stopped by user" :
688
+ "Debate ended due to error";
689
+ statusLine.innerHTML = iconHtml("check-circle") + " " + escapeHtml(reasonText) + " (" + (entry.rounds || 0) + " rounds)";
690
+ ctx.messagesEl.appendChild(statusLine);
691
+ refreshIcons();
692
+
693
+ // Show ended mode with native input area
694
+ if (ctx.showDebateEndedMode) ctx.showDebateEndedMode(entry);
667
695
  }
668
696
 
669
697
  export function renderDebateCommentInjected(entry) {
@@ -690,9 +718,73 @@ export function isDebateActive() {
690
718
  return debateActive;
691
719
  }
692
720
 
693
- // --- Debate modal ---
721
+ // --- Debate modal (wizard) ---
694
722
  var modalEl = null;
695
723
  var selectedPanelists = [];
724
+ var wizardStep = 0;
725
+
726
+ function goToStep(step) {
727
+ wizardStep = step;
728
+ if (!modalEl) return;
729
+ var pages = modalEl.querySelectorAll(".debate-wizard-page");
730
+ var dots = modalEl.querySelectorAll(".debate-step-dot");
731
+ for (var i = 0; i < pages.length; i++) {
732
+ if (i === step) {
733
+ pages[i].classList.add("active");
734
+ if (dots[i]) dots[i].classList.add("active");
735
+ } else {
736
+ pages[i].classList.remove("active");
737
+ if (dots[i]) dots[i].classList.remove("active");
738
+ }
739
+ }
740
+ var backBtn = document.getElementById("debate-modal-back");
741
+ var nextBtn = document.getElementById("debate-modal-next");
742
+ var quickBtn = document.getElementById("debate-modal-quick");
743
+ var startBtn = document.getElementById("debate-modal-start");
744
+ var cancelBtn = document.getElementById("debate-modal-cancel");
745
+ if (step === 0) {
746
+ // Cancel + (Quick / Setup Panel) - buttons hidden until topic entered
747
+ if (backBtn) backBtn.classList.add("hidden");
748
+ if (cancelBtn) cancelBtn.classList.remove("hidden");
749
+ if (quickBtn) quickBtn.classList.add("hidden");
750
+ if (nextBtn) nextBtn.classList.add("hidden");
751
+ if (startBtn) startBtn.classList.add("hidden");
752
+ } else {
753
+ // Back + (Quick / Setup Detail)
754
+ if (backBtn) backBtn.classList.remove("hidden");
755
+ if (cancelBtn) cancelBtn.classList.add("hidden");
756
+ if (quickBtn) quickBtn.classList.add("hidden");
757
+ if (nextBtn) nextBtn.classList.add("hidden");
758
+ if (startBtn) startBtn.classList.add("hidden");
759
+ }
760
+ }
761
+
762
+ function updateStepButtons() {
763
+ var topicInput = document.getElementById("debate-topic-input");
764
+ var topic = topicInput ? topicInput.value.trim() : "";
765
+ var quickBtn = document.getElementById("debate-modal-quick");
766
+ var nextBtn = document.getElementById("debate-modal-next");
767
+ var startBtn = document.getElementById("debate-modal-start");
768
+ if (wizardStep === 0) {
769
+ // Show buttons only when topic is filled
770
+ if (topic) {
771
+ if (quickBtn) quickBtn.classList.remove("hidden");
772
+ if (nextBtn) nextBtn.classList.remove("hidden");
773
+ } else {
774
+ if (quickBtn) quickBtn.classList.add("hidden");
775
+ if (nextBtn) nextBtn.classList.add("hidden");
776
+ }
777
+ } else if (wizardStep === 1) {
778
+ // Show buttons only when at least 1 panelist selected
779
+ if (selectedPanelists.length > 0) {
780
+ if (quickBtn) quickBtn.classList.remove("hidden");
781
+ if (startBtn) startBtn.classList.remove("hidden");
782
+ } else {
783
+ if (quickBtn) quickBtn.classList.add("hidden");
784
+ if (startBtn) startBtn.classList.add("hidden");
785
+ }
786
+ }
787
+ }
696
788
 
697
789
  export function openDebateModal(opts) {
698
790
  opts = opts || {};
@@ -700,36 +792,13 @@ export function openDebateModal(opts) {
700
792
  if (!modalEl) return;
701
793
 
702
794
  modalEl.classList.remove("hidden");
795
+ goToStep(0);
703
796
 
704
797
  var topicInput = document.getElementById("debate-topic-input");
705
798
  if (topicInput) {
706
799
  topicInput.value = "";
707
800
  topicInput.focus();
708
- }
709
-
710
- // Show/hide skip-setup toggle (only when dmContext is available)
711
- var skipSetupRow = document.getElementById("debate-skip-setup-row");
712
- var skipSetupToggle = document.getElementById("debate-skip-setup-toggle");
713
- if (skipSetupRow) {
714
- if (opts.dmContext) {
715
- skipSetupRow.classList.remove("hidden");
716
- } else {
717
- skipSetupRow.classList.add("hidden");
718
- }
719
- }
720
- if (skipSetupToggle) {
721
- skipSetupToggle.checked = false;
722
- skipSetupToggle.onchange = function () {
723
- var topicLabel = modalEl.querySelector('.debate-field-label[for="debate-topic-input"]');
724
- var reqSpan = topicLabel ? topicLabel.querySelector(".debate-field-req") : null;
725
- if (skipSetupToggle.checked) {
726
- if (topicInput) topicInput.placeholder = "Leave blank to auto-detect from conversation...";
727
- if (reqSpan) reqSpan.style.display = "none";
728
- } else {
729
- if (topicInput) topicInput.placeholder = "e.g. Should AI development be regulated?";
730
- if (reqSpan) reqSpan.style.display = "";
731
- }
732
- };
801
+ topicInput.oninput = function () { updateStepButtons(); };
733
802
  }
734
803
 
735
804
  // Build DM context string for quick start
@@ -763,87 +832,103 @@ export function openDebateModal(opts) {
763
832
  }
764
833
  }
765
834
 
766
- // Close button
835
+ // Close / Cancel / Backdrop
767
836
  var closeBtn = document.getElementById("debate-modal-close");
768
- if (closeBtn) {
769
- closeBtn.onclick = closeDebateModal;
770
- }
837
+ if (closeBtn) closeBtn.onclick = closeDebateModal;
771
838
  var cancelBtn = document.getElementById("debate-modal-cancel");
772
- if (cancelBtn) {
773
- cancelBtn.onclick = closeDebateModal;
774
- }
775
-
776
- // Backdrop click to close
839
+ if (cancelBtn) cancelBtn.onclick = closeDebateModal;
777
840
  var backdrop = modalEl.querySelector(".debate-modal-backdrop");
778
- if (backdrop) {
779
- backdrop.onclick = closeDebateModal;
841
+ if (backdrop) backdrop.onclick = closeDebateModal;
842
+
843
+ // Back button
844
+ var backBtn = document.getElementById("debate-modal-back");
845
+ if (backBtn) {
846
+ backBtn.onclick = function () {
847
+ if (wizardStep > 0) goToStep(wizardStep - 1);
848
+ updateStepButtons();
849
+ };
780
850
  }
781
851
 
782
- // Start button
783
- var startBtn = document.getElementById("debate-modal-start");
784
- if (startBtn) {
785
- startBtn.onclick = function () {
786
- var topic = topicInput ? topicInput.value.trim() : "";
787
- var skipSetup = skipSetupToggle && skipSetupToggle.checked;
788
-
789
- if (!skipSetup && !topic) {
790
- topicInput.focus();
791
- return;
792
- }
793
- if (selectedPanelists.length === 0) return;
852
+ // Choose Panel (step 0 -> 1)
853
+ var nextBtn = document.getElementById("debate-modal-next");
854
+ if (nextBtn) {
855
+ nextBtn.onclick = function () {
856
+ goToStep(1);
857
+ updateStepButtons();
858
+ };
859
+ }
794
860
 
795
- var currentMateId = ctx.currentMateId ? ctx.currentMateId() : null;
796
- if (!currentMateId) return;
861
+ // Helper: build payload and send
862
+ function fireDebate(quick) {
863
+ var topic = topicInput ? topicInput.value.trim() : "";
864
+ var currentMateId = ctx.currentMateId ? ctx.currentMateId() : null;
865
+ if (!currentMateId || !topic) return;
797
866
 
798
- var debatePayload = {
799
- type: "debate_start",
800
- moderatorId: currentMateId,
801
- topic: topic || (skipSetup ? "(auto-detect from conversation)" : ""),
802
- panelists: selectedPanelists.map(function (id) {
803
- return { mateId: id, role: "", brief: "" };
804
- }),
805
- };
867
+ var debatePayload = {
868
+ type: "debate_start",
869
+ moderatorId: currentMateId,
870
+ topic: topic,
871
+ quickStart: !!quick,
872
+ };
806
873
 
807
- // When skip setup is toggled, send as quick start with DM context
808
- if (skipSetup) {
809
- debatePayload.quickStart = true;
874
+ if (quick && wizardStep === 0) {
875
+ // Step 0 quick: delegate panelist selection to moderator
876
+ debatePayload.panelists = [];
877
+ debatePayload.delegatePanelists = true;
878
+ debatePayload.dmContext = dmContextStr;
879
+ } else {
880
+ // Step 1: user picked panelists
881
+ if (selectedPanelists.length === 0) return;
882
+ debatePayload.panelists = selectedPanelists.map(function (id) {
883
+ return { mateId: id, role: "", brief: "" };
884
+ });
885
+ if (quick) {
810
886
  debatePayload.dmContext = dmContextStr;
811
887
  }
888
+ }
812
889
 
813
- function sendDebate() {
814
- // Create a new session first, then send debate_start after switch
815
- if (ctx.ws) {
816
- var onMessage = function (evt) {
817
- try {
818
- var data = JSON.parse(evt.data);
819
- if (data.type === "session_switched") {
820
- ctx.ws.removeEventListener("message", onMessage);
821
- ctx.ws.send(JSON.stringify(debatePayload));
822
- }
823
- } catch (e) {}
824
- };
825
- ctx.ws.addEventListener("message", onMessage);
826
- ctx.ws.send(JSON.stringify({ type: "new_session" }));
827
- }
828
- closeDebateModal();
890
+ function sendDebate() {
891
+ if (ctx.ws) {
892
+ var onMessage = function (evt) {
893
+ try {
894
+ var data = JSON.parse(evt.data);
895
+ if (data.type === "session_switched") {
896
+ ctx.ws.removeEventListener("message", onMessage);
897
+ ctx.ws.send(JSON.stringify(debatePayload));
898
+ }
899
+ } catch (e) {}
900
+ };
901
+ ctx.ws.addEventListener("message", onMessage);
902
+ ctx.ws.send(JSON.stringify({ type: "new_session" }));
829
903
  }
904
+ closeDebateModal();
905
+ }
830
906
 
831
- if (skipSetup) {
832
- // Quick start: no skill needed
833
- sendDebate();
907
+ if (quick) {
908
+ sendDebate();
909
+ } else {
910
+ if (ctx.requireSkills) {
911
+ ctx.requireSkills({
912
+ title: "Skill Installation Required",
913
+ reason: "The Debate Setup skill is required to design this debate.",
914
+ skills: [{ name: "clay-debate-setup", url: "https://github.com/chadbyte/clay-debate-setup", scope: "global" }]
915
+ }, sendDebate);
834
916
  } else {
835
- // Standard: require clay-debate-setup skill first
836
- if (ctx.requireSkills) {
837
- ctx.requireSkills({
838
- title: "Skill Installation Required",
839
- reason: "The Debate Setup skill is required to start a debate.",
840
- skills: [{ name: "clay-debate-setup", url: "https://github.com/chadbyte/clay-debate-setup", scope: "global" }]
841
- }, sendDebate);
842
- } else {
843
- sendDebate();
844
- }
917
+ sendDebate();
845
918
  }
846
- };
919
+ }
920
+ }
921
+
922
+ // Quick Start button (both steps)
923
+ var quickBtn = document.getElementById("debate-modal-quick");
924
+ if (quickBtn) {
925
+ quickBtn.onclick = function () { fireDebate(true); };
926
+ }
927
+
928
+ // Design Debate button (step 1, full setup with skill)
929
+ var startBtn = document.getElementById("debate-modal-start");
930
+ if (startBtn) {
931
+ startBtn.onclick = function () { fireDebate(false); };
847
932
  }
848
933
 
849
934
  refreshIcons();
@@ -895,10 +980,11 @@ function createPanelItem(mate) {
895
980
  item.classList.remove("selected");
896
981
  cb.checked = false;
897
982
  }
983
+ updateStepButtons();
898
984
  }
899
985
 
900
986
  item.addEventListener("click", function (e) {
901
- if (e.target === cb) return; // let checkbox handle itself
987
+ if (e.target === cb) return;
902
988
  toggle();
903
989
  });
904
990
  cb.addEventListener("change", function () {
@@ -910,6 +996,7 @@ function createPanelItem(mate) {
910
996
  selectedPanelists.splice(idx, 1);
911
997
  item.classList.remove("selected");
912
998
  }
999
+ updateStepButtons();
913
1000
  });
914
1001
 
915
1002
  return item;