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.
- package/bin/cli.js +5 -9
- package/lib/browser-mcp-server.js +496 -0
- package/lib/daemon.js +1 -1
- package/lib/os-users.js +23 -0
- package/lib/project-debate.js +206 -88
- package/lib/project-mate-interaction.js +766 -0
- package/lib/project-memory.js +677 -0
- package/lib/project.js +546 -1361
- package/lib/public/app.js +817 -175
- package/lib/public/css/debate.css +224 -2
- package/lib/public/css/icon-strip.css +10 -10
- package/lib/public/css/input.css +263 -83
- package/lib/public/css/mates.css +56 -57
- package/lib/public/css/mention.css +7 -4
- package/lib/public/css/menus.css +7 -0
- package/lib/public/css/messages.css +17 -0
- package/lib/public/css/mobile-nav.css +3 -1
- package/lib/public/css/overlays.css +181 -0
- package/lib/public/css/rewind.css +79 -0
- package/lib/public/css/server-settings.css +1 -0
- package/lib/public/css/sidebar.css +10 -0
- package/lib/public/css/title-bar.css +189 -3
- package/lib/public/index.html +53 -16
- package/lib/public/modules/context-sources.js +313 -0
- package/lib/public/modules/debate.js +184 -97
- package/lib/public/modules/input.js +18 -1
- package/lib/public/modules/mate-knowledge.js +11 -11
- package/lib/public/modules/mate-memory.js +5 -5
- package/lib/public/modules/mate-sidebar.js +13 -9
- package/lib/public/modules/mention.js +40 -2
- package/lib/public/modules/notifications.js +109 -1
- package/lib/public/modules/rewind.js +36 -0
- package/lib/public/modules/sidebar.js +107 -28
- package/lib/public/modules/terminal.js +8 -0
- package/lib/public/modules/theme.js +2 -1
- package/lib/public/modules/tools.js +69 -24
- package/lib/sdk-bridge.js +81 -7
- package/lib/sdk-worker.js +13 -1
- package/lib/server.js +42 -0
- package/lib/sessions.js +39 -7
- package/lib/terminal-manager.js +36 -6
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
783
|
-
var
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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
|
-
|
|
796
|
-
|
|
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
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}),
|
|
805
|
-
};
|
|
867
|
+
var debatePayload = {
|
|
868
|
+
type: "debate_start",
|
|
869
|
+
moderatorId: currentMateId,
|
|
870
|
+
topic: topic,
|
|
871
|
+
quickStart: !!quick,
|
|
872
|
+
};
|
|
806
873
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
-
|
|
832
|
-
|
|
833
|
-
|
|
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
|
-
|
|
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;
|
|
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;
|