clay-server 2.26.0-beta.9 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser-mcp-server.js +4 -4
- package/lib/debate-mcp-server.js +94 -0
- package/lib/mates.js +12 -24
- package/lib/project-debate.js +304 -166
- package/lib/project-mate-interaction.js +10 -5
- package/lib/project.js +108 -39
- package/lib/public/app.js +317 -85
- package/lib/public/css/debate.css +230 -2
- package/lib/public/css/filebrowser.css +41 -4
- package/lib/public/css/icon-strip.css +10 -10
- package/lib/public/css/input.css +33 -0
- package/lib/public/css/mates.css +17 -38
- package/lib/public/css/messages.css +17 -0
- package/lib/public/css/mobile-nav.css +3 -1
- package/lib/public/css/rewind.css +17 -4
- package/lib/public/index.html +23 -15
- package/lib/public/modules/context-sources.js +21 -6
- package/lib/public/modules/debate.js +298 -97
- package/lib/public/modules/input.js +15 -0
- 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/sidebar.js +105 -26
- package/lib/public/modules/terminal.js +62 -6
- package/lib/public/modules/tools.js +2 -2
- package/lib/sdk-bridge.js +123 -22
- package/lib/sessions.js +2 -2
- package/package.json +1 -1
package/lib/public/app.js
CHANGED
|
@@ -2,7 +2,7 @@ import { avatarUrl, userAvatarUrl, mateAvatarUrl } from './modules/avatar.js';
|
|
|
2
2
|
import { showToast, copyToClipboard, escapeHtml } from './modules/utils.js';
|
|
3
3
|
import { refreshIcons, iconHtml } from './modules/icons.js';
|
|
4
4
|
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, closeMermaidModal, parseEmojis } from './modules/markdown.js';
|
|
5
|
-
import { initSidebar, renderSessionList, handleSearchResults, updateSessionPresence, updatePageTitle, populateCliSessionList, renderIconStrip, renderSidebarPresence, initIconStrip, getEmojiCategories, renderUserStrip, setCurrentDmUser, updateDmBadge, updateSessionBadge, updateProjectBadge, closeDmUserPicker, spawnDustParticles, openMobileSheet, setMobileSheetMateData } from './modules/sidebar.js';
|
|
5
|
+
import { initSidebar, renderSessionList, handleSearchResults, updateSessionPresence, updatePageTitle, populateCliSessionList, renderIconStrip, renderSidebarPresence, initIconStrip, getEmojiCategories, renderUserStrip, setCurrentDmUser, updateDmBadge, updateSessionBadge, updateProjectBadge, closeDmUserPicker, spawnDustParticles, openMobileSheet, setMobileSheetMateData, refreshMobileChatSheet } from './modules/sidebar.js';
|
|
6
6
|
import { initMateSidebar, showMateSidebar, hideMateSidebar, renderMateSessionList, updateMateSidebarProfile, handleMateSearchResults } from './modules/mate-sidebar.js';
|
|
7
7
|
import { initMateKnowledge, requestKnowledgeList, renderKnowledgeList, handleKnowledgeContent, hideKnowledge } from './modules/mate-knowledge.js';
|
|
8
8
|
import { initMateMemory, renderMemoryList, hideMemory } from './modules/mate-memory.js';
|
|
@@ -32,7 +32,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
|
|
|
32
32
|
import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } from './modules/command-palette.js';
|
|
33
33
|
import { initLongPress } from './modules/longpress.js';
|
|
34
34
|
import { initMention, handleMentionStart, handleMentionStream, handleMentionDone, handleMentionError, handleMentionActivity, renderMentionUser, renderMentionResponse } from './modules/mention.js';
|
|
35
|
-
import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateResumed, handleDebateTurn, handleDebateActivity, handleDebateStream, handleDebateTurnDone, handleDebateCommentQueued, handleDebateCommentInjected, handleDebateEnded, handleDebateError, renderDebateStarted, renderDebateTurnDone, renderDebateEnded, renderDebateCommentInjected, renderDebateUserResume, openDebateModal, closeDebateModal, handleDebateBriefReady, renderDebateBriefReady, isDebateActive, resetDebateState } from './modules/debate.js';
|
|
35
|
+
import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateResumed, handleDebateTurn, handleDebateActivity, handleDebateStream, handleDebateTurnDone, handleDebateCommentQueued, handleDebateCommentInjected, handleDebateEnded, handleDebateError, renderDebateStarted, renderDebateTurnDone, renderDebateEnded, renderDebateCommentInjected, renderDebateUserResume, openDebateModal, closeDebateModal, handleDebateBriefReady, renderDebateBriefReady, isDebateActive, resetDebateState, exportDebateAsPdf, renderMcpDebateProposal } from './modules/debate.js';
|
|
36
36
|
|
|
37
37
|
// --- Base path for multi-project routing ---
|
|
38
38
|
var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
|
|
@@ -565,6 +565,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
565
565
|
// --- DM Mode Functions ---
|
|
566
566
|
function openDm(targetUserId) {
|
|
567
567
|
if (!ws || ws.readyState !== 1) return;
|
|
568
|
+
// Persist DM state for refresh recovery
|
|
569
|
+
try { localStorage.setItem("clay-active-dm", targetUserId); } catch (e) {}
|
|
568
570
|
// Check mate skill updates before opening mate DM
|
|
569
571
|
if (typeof targetUserId === "string" && targetUserId.indexOf("mate_") === 0) {
|
|
570
572
|
showMateOnboarding(function () {
|
|
@@ -816,6 +818,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
816
818
|
dmMode = false;
|
|
817
819
|
dmKey = null;
|
|
818
820
|
dmTargetUser = null;
|
|
821
|
+
try { localStorage.removeItem("clay-active-dm"); } catch (e) {}
|
|
819
822
|
setCurrentDmUser(null);
|
|
820
823
|
|
|
821
824
|
var mainCol = document.getElementById("main-column");
|
|
@@ -1798,9 +1801,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
1798
1801
|
};
|
|
1799
1802
|
initSidebar(sidebarCtx);
|
|
1800
1803
|
initIconStrip(sidebarCtx);
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
+
var wsGetter = function () { return ws; };
|
|
1805
|
+
initMateSidebar(wsGetter);
|
|
1806
|
+
initMateKnowledge(wsGetter);
|
|
1807
|
+
initMateMemory(wsGetter, { onShow: function () { hideKnowledge(); hideNotes(); } });
|
|
1804
1808
|
initMateWizard(
|
|
1805
1809
|
function (msg) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(msg)); },
|
|
1806
1810
|
function (mate) { handleMateCreatedInApp(mate); }
|
|
@@ -3040,6 +3044,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
3040
3044
|
var div = document.createElement("div");
|
|
3041
3045
|
div.className = "msg-user" + (isOtherUser ? " msg-user-other" : "");
|
|
3042
3046
|
div.dataset.turn = ++turnCounter;
|
|
3047
|
+
if (shouldGroupMessage("msg-user")) div.classList.add("grouped");
|
|
3043
3048
|
var bubble = document.createElement("div");
|
|
3044
3049
|
bubble.className = "bubble";
|
|
3045
3050
|
bubble.dir = "auto";
|
|
@@ -3130,8 +3135,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
3130
3135
|
header.appendChild(nameSpan);
|
|
3131
3136
|
var timeSpan = document.createElement("span");
|
|
3132
3137
|
timeSpan.className = "dm-bubble-time";
|
|
3133
|
-
|
|
3134
|
-
timeSpan.textContent = String(nowH.getHours()).padStart(2, "0") + ":" + String(nowH.getMinutes()).padStart(2, "0");
|
|
3138
|
+
timeSpan.textContent = getMsgTime();
|
|
3135
3139
|
header.appendChild(timeSpan);
|
|
3136
3140
|
contentWrap.appendChild(header);
|
|
3137
3141
|
contentWrap.appendChild(bubble);
|
|
@@ -3140,10 +3144,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
3140
3144
|
// Action bar below bubble (icons visible on hover)
|
|
3141
3145
|
var actions = document.createElement("div");
|
|
3142
3146
|
actions.className = "msg-actions";
|
|
3143
|
-
var now = new Date();
|
|
3144
|
-
var timeStr = String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0");
|
|
3145
3147
|
actions.innerHTML =
|
|
3146
|
-
'<span class="msg-action-time">' +
|
|
3148
|
+
'<span class="msg-action-time">' + getMsgTime() + '</span>' +
|
|
3147
3149
|
'<button class="msg-action-btn msg-action-copy" type="button" title="Copy">' + iconHtml("copy") + '</button>' +
|
|
3148
3150
|
'<button class="msg-action-btn msg-action-fork" type="button" title="Fork">' + iconHtml("git-branch") + '</button>' +
|
|
3149
3151
|
'<button class="msg-action-btn msg-action-rewind msg-user-rewind-btn" type="button" title="Rewind">' + iconHtml("rotate-ccw") + '</button>' +
|
|
@@ -3164,11 +3166,33 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
3164
3166
|
forceScrollToBottom();
|
|
3165
3167
|
}
|
|
3166
3168
|
|
|
3169
|
+
// Track the timestamp of the current message being processed (from history _ts or now)
|
|
3170
|
+
var currentMsgTs = null;
|
|
3171
|
+
|
|
3172
|
+
function getMsgTime() {
|
|
3173
|
+
var d = currentMsgTs ? new Date(currentMsgTs) : new Date();
|
|
3174
|
+
return String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
function shouldGroupMessage(senderClass) {
|
|
3178
|
+
// Skip grouping during history replay if no timestamp data
|
|
3179
|
+
if (replayingHistory && !currentMsgTs) return false;
|
|
3180
|
+
var prev = messagesEl.lastElementChild;
|
|
3181
|
+
if (!prev || !prev.classList.contains(senderClass)) return false;
|
|
3182
|
+
var prevTime = prev.querySelector(".dm-bubble-time");
|
|
3183
|
+
if (!prevTime) return false;
|
|
3184
|
+
return prevTime.textContent === getMsgTime();
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3167
3187
|
function ensureAssistantBlock() {
|
|
3168
3188
|
if (!currentMsgEl) {
|
|
3169
3189
|
currentMsgEl = document.createElement("div");
|
|
3170
3190
|
currentMsgEl.className = "msg-assistant";
|
|
3171
3191
|
currentMsgEl.dataset.turn = turnCounter;
|
|
3192
|
+
|
|
3193
|
+
var grouped = shouldGroupMessage("msg-assistant");
|
|
3194
|
+
if (grouped) currentMsgEl.classList.add("grouped");
|
|
3195
|
+
|
|
3172
3196
|
// Always render avatar + header structure (CSS controls visibility)
|
|
3173
3197
|
var _isDm2 = document.body.classList.contains("mate-dm-active") && document.body.dataset.mateAvatarUrl;
|
|
3174
3198
|
var avi = document.createElement("img");
|
|
@@ -3187,8 +3211,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
3187
3211
|
header.appendChild(nameSpan);
|
|
3188
3212
|
var timeSpan = document.createElement("span");
|
|
3189
3213
|
timeSpan.className = "dm-bubble-time";
|
|
3190
|
-
|
|
3191
|
-
timeSpan.textContent = String(nowA.getHours()).padStart(2, "0") + ":" + String(nowA.getMinutes()).padStart(2, "0");
|
|
3214
|
+
timeSpan.textContent = getMsgTime();
|
|
3192
3215
|
header.appendChild(timeSpan);
|
|
3193
3216
|
contentWrap.appendChild(header);
|
|
3194
3217
|
|
|
@@ -3692,7 +3715,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
3692
3715
|
// Auto-switch input to schedule mode: any message typed will be queued for after reset
|
|
3693
3716
|
var delayUntilReset = msg.resetsAt - Date.now();
|
|
3694
3717
|
if (delayUntilReset > 0) {
|
|
3695
|
-
setScheduleDelayMs(delayUntilReset +
|
|
3718
|
+
setScheduleDelayMs(delayUntilReset + 60000); // +1min buffer after reset
|
|
3696
3719
|
}
|
|
3697
3720
|
rateLimitResetTimer = setTimeout(function () {
|
|
3698
3721
|
rateLimitResetsAt = null;
|
|
@@ -4120,6 +4143,31 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4120
4143
|
|
|
4121
4144
|
// Session restore is now server-driven (user-presence.json).
|
|
4122
4145
|
// Mate DM restore is also server-driven via "restore_mate_dm" message.
|
|
4146
|
+
// Fallback: if server doesn't restore DM within 2s, try localStorage
|
|
4147
|
+
var savedDm = null;
|
|
4148
|
+
try { savedDm = localStorage.getItem("clay-active-dm"); } catch (e) {}
|
|
4149
|
+
if (savedDm && !dmMode && !mateProjectSlug) {
|
|
4150
|
+
var dmFallbackTimer = setTimeout(function () {
|
|
4151
|
+
if (!dmMode && savedDm) {
|
|
4152
|
+
console.log("[dm-restore] Server did not restore DM, using localStorage fallback:", savedDm);
|
|
4153
|
+
openDm(savedDm);
|
|
4154
|
+
}
|
|
4155
|
+
}, 2000);
|
|
4156
|
+
// Cancel fallback if server restores DM first
|
|
4157
|
+
var origHandler = ws.onmessage;
|
|
4158
|
+
var patchedOnce = false;
|
|
4159
|
+
var checkRestore = function (evt) {
|
|
4160
|
+
try {
|
|
4161
|
+
var d = JSON.parse(evt.data);
|
|
4162
|
+
if (d.type === "restore_mate_dm" && !patchedOnce) {
|
|
4163
|
+
patchedOnce = true;
|
|
4164
|
+
clearTimeout(dmFallbackTimer);
|
|
4165
|
+
}
|
|
4166
|
+
} catch (e) {}
|
|
4167
|
+
};
|
|
4168
|
+
ws.addEventListener("message", checkRestore);
|
|
4169
|
+
setTimeout(function () { ws.removeEventListener("message", checkRestore); }, 3000);
|
|
4170
|
+
}
|
|
4123
4171
|
// Safety: clear returningFromMateDm after initial messages settle
|
|
4124
4172
|
// (handles case where we connect to a non-main project that won't send restore_mate_dm)
|
|
4125
4173
|
if (returningFromMateDm) {
|
|
@@ -4218,6 +4266,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4218
4266
|
}
|
|
4219
4267
|
|
|
4220
4268
|
function processMessage(msg) {
|
|
4269
|
+
// Preserve original timestamp from history replay
|
|
4270
|
+
currentMsgTs = msg._ts || null;
|
|
4221
4271
|
var isMateDm = dmMode && dmTargetUser && dmTargetUser.isMate;
|
|
4222
4272
|
|
|
4223
4273
|
// DEBUG: trace session/history loading
|
|
@@ -4232,6 +4282,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4232
4282
|
if (isMateDm) {
|
|
4233
4283
|
if (msg.type === "session_list") {
|
|
4234
4284
|
renderMateSessionList(msg.sessions || []);
|
|
4285
|
+
refreshMobileChatSheet();
|
|
4235
4286
|
// Override title bar with mate name and re-apply color
|
|
4236
4287
|
var _mdn = (dmTargetUser.displayName || "New Mate");
|
|
4237
4288
|
if (headerTitleEl) headerTitleEl.textContent = _mdn;
|
|
@@ -4337,6 +4388,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4337
4388
|
if (dhBar) dhBar.remove();
|
|
4338
4389
|
var dbBadges = document.querySelectorAll(".debate-header-badge");
|
|
4339
4390
|
for (var dbi = 0; dbi < dbBadges.length; dbi++) dbBadges[dbi].remove();
|
|
4391
|
+
// Clean up ended mode banner if debate is not active on this session
|
|
4392
|
+
if (debateEndedMode) exitDebateEndedMode();
|
|
4340
4393
|
}
|
|
4341
4394
|
scrollToBottom();
|
|
4342
4395
|
// Scroll to tool element if navigating from file edit history
|
|
@@ -4577,9 +4630,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4577
4630
|
break;
|
|
4578
4631
|
|
|
4579
4632
|
case "session_list":
|
|
4580
|
-
|
|
4581
|
-
renderMateSessionList(msg.sessions || []);
|
|
4582
|
-
}
|
|
4633
|
+
renderMateSessionList(msg.sessions || []);
|
|
4583
4634
|
renderSessionList(msg.sessions || []);
|
|
4584
4635
|
handlePaletteSessionSwitch();
|
|
4585
4636
|
break;
|
|
@@ -4670,6 +4721,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4670
4721
|
break;
|
|
4671
4722
|
|
|
4672
4723
|
case "user_message":
|
|
4724
|
+
if (msg._internal) break;
|
|
4673
4725
|
resetThinkingGroup();
|
|
4674
4726
|
if (msg.planContent) {
|
|
4675
4727
|
setPlanContent(msg.planContent);
|
|
@@ -4691,7 +4743,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4691
4743
|
header.className = "context-card-header";
|
|
4692
4744
|
var icon = document.createElement("span");
|
|
4693
4745
|
icon.className = "context-card-icon";
|
|
4694
|
-
icon.
|
|
4746
|
+
icon.innerHTML = iconHtml("globe");
|
|
4695
4747
|
header.appendChild(icon);
|
|
4696
4748
|
var label = document.createElement("span");
|
|
4697
4749
|
label.textContent = "Viewing tab";
|
|
@@ -4715,6 +4767,15 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4715
4767
|
if (tabTitle || tabDomain) {
|
|
4716
4768
|
var meta = document.createElement("div");
|
|
4717
4769
|
meta.className = "context-card-meta";
|
|
4770
|
+
if (msg.tab.favIconUrl) {
|
|
4771
|
+
var fav = document.createElement("img");
|
|
4772
|
+
fav.className = "context-card-favicon";
|
|
4773
|
+
fav.src = msg.tab.favIconUrl;
|
|
4774
|
+
fav.width = 14;
|
|
4775
|
+
fav.height = 14;
|
|
4776
|
+
fav.onerror = function () { this.style.display = "none"; };
|
|
4777
|
+
meta.appendChild(fav);
|
|
4778
|
+
}
|
|
4718
4779
|
var titleEl = document.createElement("span");
|
|
4719
4780
|
titleEl.className = "context-card-title";
|
|
4720
4781
|
titleEl.textContent = tabTitle;
|
|
@@ -4788,6 +4849,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4788
4849
|
}
|
|
4789
4850
|
renderPlanBanner("exit");
|
|
4790
4851
|
getTools()[msg.id] = { el: null, name: msg.name, input: null, done: true, hidden: true };
|
|
4852
|
+
} else if (msg.name === "propose_debate" || (msg.name && msg.name.indexOf("propose_debate") !== -1)) {
|
|
4853
|
+
getTools()[msg.id] = { el: null, name: msg.name, input: null, done: true, hidden: true };
|
|
4791
4854
|
} else if (getTodoTools()[msg.name]) {
|
|
4792
4855
|
getTools()[msg.id] = { el: null, name: msg.name, input: null, done: true, hidden: true };
|
|
4793
4856
|
} else {
|
|
@@ -4796,7 +4859,18 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4796
4859
|
break;
|
|
4797
4860
|
|
|
4798
4861
|
case "tool_executing":
|
|
4799
|
-
if (msg.name === "
|
|
4862
|
+
if ((msg.name === "propose_debate" || (msg.name && msg.name.indexOf("propose_debate") !== -1)) && msg.input) {
|
|
4863
|
+
var _dpTool = getTools()[msg.id];
|
|
4864
|
+
if (_dpTool) {
|
|
4865
|
+
if (_dpTool.el) _dpTool.el.style.display = "none";
|
|
4866
|
+
_dpTool.done = true;
|
|
4867
|
+
_dpTool.hidden = true;
|
|
4868
|
+
removeToolFromGroup(msg.id);
|
|
4869
|
+
}
|
|
4870
|
+
finalizeAssistantBlock();
|
|
4871
|
+
renderMcpDebateProposal(msg.id, msg.input);
|
|
4872
|
+
startUrgentBlink();
|
|
4873
|
+
} else if (msg.name === "AskUserQuestion" && msg.input && msg.input.questions) {
|
|
4800
4874
|
var askTool = getTools()[msg.id];
|
|
4801
4875
|
if (askTool) {
|
|
4802
4876
|
if (askTool.el) askTool.el.style.display = "none";
|
|
@@ -5353,6 +5427,23 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
5353
5427
|
break;
|
|
5354
5428
|
|
|
5355
5429
|
// --- @Mention ---
|
|
5430
|
+
case "mention_processing":
|
|
5431
|
+
// Broadcast: show/hide activity dot on mate avatar across all tabs
|
|
5432
|
+
if (msg.mateId) {
|
|
5433
|
+
var mateContainers = document.querySelectorAll('.icon-strip-mate[data-user-id="' + msg.mateId + '"]');
|
|
5434
|
+
for (var mi = 0; mi < mateContainers.length; mi++) {
|
|
5435
|
+
var dot = mateContainers[mi].querySelector(".icon-strip-status");
|
|
5436
|
+
if (msg.active) {
|
|
5437
|
+
if (dot) dot.classList.add("processing");
|
|
5438
|
+
mateContainers[mi].classList.add("mention-active");
|
|
5439
|
+
} else {
|
|
5440
|
+
if (dot) dot.classList.remove("processing");
|
|
5441
|
+
mateContainers[mi].classList.remove("mention-active");
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
}
|
|
5445
|
+
break;
|
|
5446
|
+
|
|
5356
5447
|
case "mention_start":
|
|
5357
5448
|
handleMentionStart(msg);
|
|
5358
5449
|
break;
|
|
@@ -5430,6 +5521,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
5430
5521
|
}
|
|
5431
5522
|
break;
|
|
5432
5523
|
|
|
5524
|
+
case "debate_hand_raised":
|
|
5525
|
+
// Visual feedback: hand is raised, waiting for floor
|
|
5526
|
+
break;
|
|
5527
|
+
|
|
5433
5528
|
case "debate_comment_queued":
|
|
5434
5529
|
handleDebateCommentQueued(msg);
|
|
5435
5530
|
break;
|
|
@@ -5446,6 +5541,14 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
5446
5541
|
showDebateConcludeConfirm(msg);
|
|
5447
5542
|
break;
|
|
5448
5543
|
|
|
5544
|
+
case "debate_user_floor":
|
|
5545
|
+
showDebateUserFloor(msg);
|
|
5546
|
+
break;
|
|
5547
|
+
|
|
5548
|
+
case "debate_user_floor_done":
|
|
5549
|
+
renderDebateUserFloorDone(msg);
|
|
5550
|
+
break;
|
|
5551
|
+
|
|
5449
5552
|
case "debate_user_resume":
|
|
5450
5553
|
renderDebateUserResume(msg);
|
|
5451
5554
|
break;
|
|
@@ -5791,6 +5894,12 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
5791
5894
|
isDmMode: function () { return dmMode && !(dmTargetUser && dmTargetUser.isMate); },
|
|
5792
5895
|
getDmKey: function () { return dmKey; },
|
|
5793
5896
|
handleDmSend: function () { handleDmSend(); },
|
|
5897
|
+
isDebateEndedMode: function () { return debateEndedMode; },
|
|
5898
|
+
handleDebateEndedSend: function () { handleDebateEndedSend(); },
|
|
5899
|
+
isDebateConcludeMode: function () { return false; },
|
|
5900
|
+
handleDebateConcludeSend: null,
|
|
5901
|
+
isDebateFloorMode: function () { return debateFloorMode; },
|
|
5902
|
+
handleDebateFloorSend: function () { handleDebateFloorSend(); },
|
|
5794
5903
|
isMateDm: function () { return dmMode && dmTargetUser && dmTargetUser.isMate; },
|
|
5795
5904
|
getMateName: function () { return dmTargetUser ? (dmTargetUser.displayName || "Mate") : "Mate"; },
|
|
5796
5905
|
getMateAvatarUrl: function () { return document.body.dataset.mateAvatarUrl || ""; },
|
|
@@ -5817,13 +5926,16 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
5817
5926
|
// --- Debate module ---
|
|
5818
5927
|
initDebate({
|
|
5819
5928
|
get ws() { return ws; },
|
|
5929
|
+
sendWs: function (obj) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(obj)); },
|
|
5820
5930
|
messagesEl: messagesEl,
|
|
5931
|
+
addToMessages: function (el) { addToMessages(el); },
|
|
5821
5932
|
scrollToBottom: scrollToBottom,
|
|
5822
5933
|
addCopyHandler: addCopyHandler,
|
|
5823
5934
|
matesList: function () { return cachedMatesList || []; },
|
|
5824
5935
|
availableBuiltins: function () { return cachedAvailableBuiltins || []; },
|
|
5825
5936
|
currentMateId: function () { return (dmTargetUser && dmTargetUser.isMate) ? dmTargetUser.id : null; },
|
|
5826
5937
|
requireSkills: requireSkills,
|
|
5938
|
+
showDebateEndedMode: function (msg) { showDebateEndedMode(msg); },
|
|
5827
5939
|
});
|
|
5828
5940
|
|
|
5829
5941
|
// --- STT module (voice input via Web Speech API) ---
|
|
@@ -7114,6 +7226,159 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7114
7226
|
scrollToBottom();
|
|
7115
7227
|
}
|
|
7116
7228
|
|
|
7229
|
+
var debateFloorMode = false;
|
|
7230
|
+
|
|
7231
|
+
var debateEndedMode = false;
|
|
7232
|
+
|
|
7233
|
+
function showDebateEndedMode(msg) {
|
|
7234
|
+
debateEndedMode = true;
|
|
7235
|
+
removeDebateBottomBar();
|
|
7236
|
+
var inputArea = document.getElementById("input-area");
|
|
7237
|
+
if (inputArea) {
|
|
7238
|
+
inputArea.classList.add("debate-floor-mode");
|
|
7239
|
+
inputArea.style.display = "";
|
|
7240
|
+
}
|
|
7241
|
+
var existingBanner = document.getElementById("debate-floor-banner");
|
|
7242
|
+
if (existingBanner) existingBanner.remove();
|
|
7243
|
+
var banner = document.createElement("div");
|
|
7244
|
+
banner.id = "debate-floor-banner";
|
|
7245
|
+
banner.className = "debate-floor-banner";
|
|
7246
|
+
banner.innerHTML = iconHtml("check-circle") + " <span>Debate ended</span>" +
|
|
7247
|
+
'<button class="debate-floor-done-btn" id="debate-ended-resume-btn">Resume</button>' +
|
|
7248
|
+
'<button class="debate-floor-done-btn" id="debate-ended-pdf-btn">' + iconHtml("download") + ' PDF</button>';
|
|
7249
|
+
if (inputArea && inputArea.parentNode) {
|
|
7250
|
+
inputArea.parentNode.insertBefore(banner, inputArea);
|
|
7251
|
+
}
|
|
7252
|
+
refreshIcons();
|
|
7253
|
+
// Resume button
|
|
7254
|
+
var resumeBtn = document.getElementById("debate-ended-resume-btn");
|
|
7255
|
+
if (resumeBtn) {
|
|
7256
|
+
resumeBtn.addEventListener("click", function () {
|
|
7257
|
+
handleDebateEndedSend();
|
|
7258
|
+
});
|
|
7259
|
+
}
|
|
7260
|
+
// PDF button
|
|
7261
|
+
var pdfBtn = document.getElementById("debate-ended-pdf-btn");
|
|
7262
|
+
if (pdfBtn) {
|
|
7263
|
+
pdfBtn.addEventListener("click", function () {
|
|
7264
|
+
pdfBtn.disabled = true;
|
|
7265
|
+
exportDebateAsPdf().then(function () { pdfBtn.disabled = false; }).catch(function () { pdfBtn.disabled = false; });
|
|
7266
|
+
});
|
|
7267
|
+
}
|
|
7268
|
+
var inputEl2 = document.getElementById("input");
|
|
7269
|
+
if (inputEl2) {
|
|
7270
|
+
inputEl2._origPlaceholder = inputEl2._origPlaceholder || inputEl2.placeholder;
|
|
7271
|
+
inputEl2.placeholder = "Continue with a new direction...";
|
|
7272
|
+
}
|
|
7273
|
+
scrollToBottom();
|
|
7274
|
+
}
|
|
7275
|
+
|
|
7276
|
+
function exitDebateEndedMode() {
|
|
7277
|
+
debateEndedMode = false;
|
|
7278
|
+
var inputArea = document.getElementById("input-area");
|
|
7279
|
+
if (inputArea) inputArea.classList.remove("debate-floor-mode");
|
|
7280
|
+
var banner = document.getElementById("debate-floor-banner");
|
|
7281
|
+
if (banner) banner.remove();
|
|
7282
|
+
var inputEl2 = document.getElementById("input");
|
|
7283
|
+
if (inputEl2 && inputEl2._origPlaceholder) {
|
|
7284
|
+
inputEl2.placeholder = inputEl2._origPlaceholder;
|
|
7285
|
+
delete inputEl2._origPlaceholder;
|
|
7286
|
+
}
|
|
7287
|
+
}
|
|
7288
|
+
|
|
7289
|
+
function handleDebateEndedSend() {
|
|
7290
|
+
var text = inputEl.value.trim();
|
|
7291
|
+
if (ws && ws.readyState === 1) {
|
|
7292
|
+
ws.send(JSON.stringify({ type: "debate_conclude_response", action: "continue", text: text }));
|
|
7293
|
+
}
|
|
7294
|
+
inputEl.value = "";
|
|
7295
|
+
exitDebateEndedMode();
|
|
7296
|
+
}
|
|
7297
|
+
|
|
7298
|
+
function showDebateUserFloor(msg) {
|
|
7299
|
+
debateFloorMode = true;
|
|
7300
|
+
// Remove debate bottom bar and show input area in floor mode
|
|
7301
|
+
removeDebateBottomBar();
|
|
7302
|
+
var inputArea = document.getElementById("input-area");
|
|
7303
|
+
if (inputArea) {
|
|
7304
|
+
inputArea.classList.add("debate-floor-mode");
|
|
7305
|
+
inputArea.style.display = "";
|
|
7306
|
+
}
|
|
7307
|
+
// Add floor banner above input
|
|
7308
|
+
var existingBanner = document.getElementById("debate-floor-banner");
|
|
7309
|
+
if (existingBanner) existingBanner.remove();
|
|
7310
|
+
var banner = document.createElement("div");
|
|
7311
|
+
banner.id = "debate-floor-banner";
|
|
7312
|
+
banner.className = "debate-floor-banner";
|
|
7313
|
+
banner.innerHTML = iconHtml("mic") + " <span>You have the floor</span>" +
|
|
7314
|
+
'<button class="debate-floor-done-btn" id="debate-floor-done-btn">Pass</button>';
|
|
7315
|
+
if (inputArea && inputArea.parentNode) {
|
|
7316
|
+
inputArea.parentNode.insertBefore(banner, inputArea);
|
|
7317
|
+
}
|
|
7318
|
+
refreshIcons();
|
|
7319
|
+
// Done button: exit floor mode without sending
|
|
7320
|
+
var doneBtn = document.getElementById("debate-floor-done-btn");
|
|
7321
|
+
if (doneBtn) {
|
|
7322
|
+
doneBtn.addEventListener("click", function () {
|
|
7323
|
+
// Pass without speaking: resume debate
|
|
7324
|
+
if (ws && ws.readyState === 1) {
|
|
7325
|
+
ws.send(JSON.stringify({ type: "debate_user_floor_response", text: "(The user passed without speaking)" }));
|
|
7326
|
+
}
|
|
7327
|
+
exitDebateFloorMode();
|
|
7328
|
+
showDebateBottomBar("live");
|
|
7329
|
+
});
|
|
7330
|
+
}
|
|
7331
|
+
// Update placeholder
|
|
7332
|
+
var inputEl = document.getElementById("input");
|
|
7333
|
+
if (inputEl) {
|
|
7334
|
+
inputEl._origPlaceholder = inputEl.placeholder;
|
|
7335
|
+
inputEl.placeholder = "Share your thoughts with the panel...";
|
|
7336
|
+
inputEl.focus();
|
|
7337
|
+
}
|
|
7338
|
+
scrollToBottom();
|
|
7339
|
+
}
|
|
7340
|
+
|
|
7341
|
+
function exitDebateFloorMode() {
|
|
7342
|
+
debateFloorMode = false;
|
|
7343
|
+
var inputArea = document.getElementById("input-area");
|
|
7344
|
+
if (inputArea) inputArea.classList.remove("debate-floor-mode");
|
|
7345
|
+
var banner = document.getElementById("debate-floor-banner");
|
|
7346
|
+
if (banner) banner.remove();
|
|
7347
|
+
var inputEl = document.getElementById("input");
|
|
7348
|
+
if (inputEl && inputEl._origPlaceholder) {
|
|
7349
|
+
inputEl.placeholder = inputEl._origPlaceholder;
|
|
7350
|
+
delete inputEl._origPlaceholder;
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
|
|
7354
|
+
function handleDebateFloorSend() {
|
|
7355
|
+
var text = inputEl.value.trim();
|
|
7356
|
+
if (!text) return;
|
|
7357
|
+
if (ws && ws.readyState === 1) {
|
|
7358
|
+
ws.send(JSON.stringify({ type: "debate_user_floor_response", text: text }));
|
|
7359
|
+
}
|
|
7360
|
+
inputEl.value = "";
|
|
7361
|
+
exitDebateFloorMode();
|
|
7362
|
+
showDebateBottomBar("live");
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7365
|
+
function renderDebateUserFloorDone(msg) {
|
|
7366
|
+
if (!messagesEl) return;
|
|
7367
|
+
var el = document.createElement("div");
|
|
7368
|
+
el.className = "debate-user-comment";
|
|
7369
|
+
var label = document.createElement("span");
|
|
7370
|
+
label.className = "debate-comment-label";
|
|
7371
|
+
label.innerHTML = iconHtml("mic") + " User:";
|
|
7372
|
+
var textEl = document.createElement("div");
|
|
7373
|
+
textEl.className = "debate-comment-text";
|
|
7374
|
+
textEl.textContent = msg.text || "";
|
|
7375
|
+
el.appendChild(label);
|
|
7376
|
+
el.appendChild(textEl);
|
|
7377
|
+
messagesEl.appendChild(el);
|
|
7378
|
+
refreshIcons();
|
|
7379
|
+
scrollToBottom();
|
|
7380
|
+
}
|
|
7381
|
+
|
|
7117
7382
|
// Legacy handler kept for compatibility
|
|
7118
7383
|
function showDebateSticky(phase, msg) {
|
|
7119
7384
|
if (phase === "ended" || phase === "hide") {
|
|
@@ -7136,7 +7401,13 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7136
7401
|
return;
|
|
7137
7402
|
}
|
|
7138
7403
|
|
|
7139
|
-
//
|
|
7404
|
+
// Show bottom bar regardless of header availability
|
|
7405
|
+
if (phase === "live") {
|
|
7406
|
+
debateHandRaiseOpen = false;
|
|
7407
|
+
showDebateBottomBar("live");
|
|
7408
|
+
}
|
|
7409
|
+
|
|
7410
|
+
// Add badges next to header title (optional, may not exist on mobile)
|
|
7140
7411
|
var headerTitle = document.getElementById("header-title");
|
|
7141
7412
|
if (!headerTitle) return;
|
|
7142
7413
|
|
|
@@ -7156,9 +7427,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7156
7427
|
roundBadge.id = "debate-header-round";
|
|
7157
7428
|
roundBadge.textContent = "R" + ((msg && msg.round) || 1);
|
|
7158
7429
|
liveBadge.after(roundBadge);
|
|
7159
|
-
|
|
7160
|
-
debateHandRaiseOpen = false;
|
|
7161
|
-
showDebateBottomBar("live");
|
|
7162
7430
|
}
|
|
7163
7431
|
}
|
|
7164
7432
|
|
|
@@ -7177,6 +7445,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7177
7445
|
bar.innerHTML =
|
|
7178
7446
|
'<div class="debate-bottom-inner">' +
|
|
7179
7447
|
'<button class="debate-bottom-hand" id="debate-bottom-hand">' + iconHtml("hand") + ' Raise hand</button>' +
|
|
7448
|
+
'<span class="debate-bottom-waiting hidden" id="debate-bottom-waiting">' + iconHtml("loader") + ' You will get the floor after the current speaker</span>' +
|
|
7180
7449
|
'<button class="debate-bottom-stop" id="debate-bottom-stop">' + iconHtml("square") + ' Stop</button>' +
|
|
7181
7450
|
'</div>';
|
|
7182
7451
|
|
|
@@ -7184,6 +7453,14 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7184
7453
|
inputArea.style.display = "none";
|
|
7185
7454
|
refreshIcons();
|
|
7186
7455
|
|
|
7456
|
+
// Restore raised state if hand was already raised
|
|
7457
|
+
if (debateHandRaiseOpen) {
|
|
7458
|
+
var handBtn = document.getElementById("debate-bottom-hand");
|
|
7459
|
+
var waitingEl = document.getElementById("debate-bottom-waiting");
|
|
7460
|
+
if (handBtn) { handBtn.classList.add("raised"); handBtn.classList.add("hidden"); }
|
|
7461
|
+
if (waitingEl) waitingEl.classList.remove("hidden");
|
|
7462
|
+
}
|
|
7463
|
+
|
|
7187
7464
|
document.getElementById("debate-bottom-hand").addEventListener("click", function () {
|
|
7188
7465
|
toggleDebateHandRaise();
|
|
7189
7466
|
});
|
|
@@ -7223,16 +7500,12 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7223
7500
|
showDebateBottomBar("live");
|
|
7224
7501
|
});
|
|
7225
7502
|
if (textArea) {
|
|
7226
|
-
textArea.focus();
|
|
7227
7503
|
textArea.addEventListener("keydown", function (e) {
|
|
7228
7504
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
7229
7505
|
e.preventDefault();
|
|
7230
7506
|
document.getElementById("debate-bottom-continue").click();
|
|
7231
7507
|
}
|
|
7232
7508
|
});
|
|
7233
|
-
textArea.addEventListener("input", function () {
|
|
7234
|
-
debateAutoResize(textArea, 12);
|
|
7235
|
-
});
|
|
7236
7509
|
}
|
|
7237
7510
|
}
|
|
7238
7511
|
}
|
|
@@ -7253,74 +7526,33 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
7253
7526
|
var handBar = document.getElementById("debate-hand-raise-bar");
|
|
7254
7527
|
if (handBar) handBar.remove();
|
|
7255
7528
|
debateHandRaiseOpen = false;
|
|
7529
|
+
// Clean up floor/ended modes
|
|
7530
|
+
if (debateFloorMode) exitDebateFloorMode();
|
|
7531
|
+
if (debateEndedMode) exitDebateEndedMode();
|
|
7256
7532
|
// Restore input area
|
|
7257
7533
|
var inputArea = document.getElementById("input-area");
|
|
7258
7534
|
if (inputArea) inputArea.style.display = "";
|
|
7259
7535
|
}
|
|
7260
7536
|
|
|
7261
7537
|
function toggleDebateHandRaise(forceState) {
|
|
7262
|
-
var
|
|
7263
|
-
debateHandRaiseOpen =
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
if (
|
|
7273
|
-
|
|
7274
|
-
}
|
|
7275
|
-
|
|
7276
|
-
// Create hand raise bar above input area
|
|
7277
|
-
var bar = document.createElement("div");
|
|
7278
|
-
bar.id = "debate-hand-raise-bar";
|
|
7279
|
-
bar.className = "debate-hand-raise-bar";
|
|
7280
|
-
bar.innerHTML =
|
|
7281
|
-
'<div class="debate-hand-raise-inner">' +
|
|
7282
|
-
'<span class="debate-hand-raise-label">' + iconHtml("hand") + ' Your comment:</span>' +
|
|
7283
|
-
'<textarea class="debate-hand-input" rows="1" placeholder="Type your comment..."></textarea>' +
|
|
7284
|
-
'<button class="debate-hand-send">Send</button>' +
|
|
7285
|
-
'<button class="debate-hand-cancel">Cancel</button>' +
|
|
7286
|
-
'</div>';
|
|
7287
|
-
|
|
7288
|
-
var inputArea = document.getElementById("input-area");
|
|
7289
|
-
if (inputArea && inputArea.parentNode) {
|
|
7290
|
-
inputArea.parentNode.insertBefore(bar, inputArea);
|
|
7291
|
-
}
|
|
7292
|
-
refreshIcons();
|
|
7293
|
-
|
|
7294
|
-
var textarea = bar.querySelector(".debate-hand-input");
|
|
7295
|
-
var sendBtn = bar.querySelector(".debate-hand-send");
|
|
7296
|
-
var cancelBtn = bar.querySelector(".debate-hand-cancel");
|
|
7297
|
-
|
|
7298
|
-
if (textarea) {
|
|
7299
|
-
textarea.focus();
|
|
7300
|
-
textarea.addEventListener("input", function () {
|
|
7301
|
-
debateAutoResize(textarea, 12);
|
|
7302
|
-
});
|
|
7538
|
+
var raise = typeof forceState === "boolean" ? forceState : !debateHandRaiseOpen;
|
|
7539
|
+
debateHandRaiseOpen = raise;
|
|
7540
|
+
|
|
7541
|
+
// Update UI: hide hand button, show waiting message
|
|
7542
|
+
var handBtn = document.getElementById("debate-bottom-hand");
|
|
7543
|
+
var waitingEl = document.getElementById("debate-bottom-waiting");
|
|
7544
|
+
if (raise) {
|
|
7545
|
+
if (handBtn) { handBtn.classList.add("raised"); handBtn.classList.add("hidden"); }
|
|
7546
|
+
if (waitingEl) waitingEl.classList.remove("hidden");
|
|
7547
|
+
} else {
|
|
7548
|
+
if (handBtn) { handBtn.classList.remove("raised"); handBtn.classList.remove("hidden"); }
|
|
7549
|
+
if (waitingEl) waitingEl.classList.add("hidden");
|
|
7303
7550
|
}
|
|
7304
7551
|
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
if (!text) return;
|
|
7308
|
-
if (ws && ws.readyState === 1) {
|
|
7309
|
-
ws.send(JSON.stringify({ type: "debate_comment", text: text }));
|
|
7310
|
-
}
|
|
7311
|
-
toggleDebateHandRaise(false);
|
|
7312
|
-
});
|
|
7313
|
-
|
|
7314
|
-
cancelBtn.addEventListener("click", function () {
|
|
7315
|
-
toggleDebateHandRaise(false);
|
|
7316
|
-
});
|
|
7317
|
-
|
|
7318
|
-
if (textarea) {
|
|
7319
|
-
textarea.addEventListener("keydown", function (e) {
|
|
7320
|
-
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendBtn.click(); }
|
|
7321
|
-
if (e.key === "Escape") { toggleDebateHandRaise(false); }
|
|
7322
|
-
});
|
|
7552
|
+
if (raise && ws && ws.readyState === 1) {
|
|
7553
|
+
ws.send(JSON.stringify({ type: "debate_hand_raise" }));
|
|
7323
7554
|
}
|
|
7555
|
+
// Floor mode will be activated when server sends debate_user_floor
|
|
7324
7556
|
}
|
|
7325
7557
|
|
|
7326
7558
|
function sendDebateStickyComment() {
|