clay-server 2.23.0 → 2.23.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/public/app.js CHANGED
@@ -31,7 +31,7 @@ import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } fr
31
31
  import { initCommandPalette, handlePaletteSessionSwitch, setPaletteVersion } from './modules/command-palette.js';
32
32
  import { initLongPress } from './modules/longpress.js';
33
33
  import { initMention, handleMentionStart, handleMentionStream, handleMentionDone, handleMentionError, handleMentionActivity, renderMentionUser, renderMentionResponse } from './modules/mention.js';
34
- import { initDebate, handleDebateStarted, handleDebateResumed, handleDebateTurn, handleDebateActivity, handleDebateStream, handleDebateTurnDone, handleDebateCommentQueued, handleDebateCommentInjected, handleDebateEnded, handleDebateError, renderDebateStarted, renderDebateTurnDone, renderDebateEnded, renderDebateCommentInjected, renderDebateUserResume, openDebateModal, closeDebateModal, openQuickDebateModal, handleDebateBriefReady, renderDebateBriefReady, isDebateActive, resetDebateState } from './modules/debate.js';
34
+ 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
35
 
36
36
  // --- Base path for multi-project routing ---
37
37
  var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
@@ -4979,6 +4979,7 @@ import { initDebate, handleDebateStarted, handleDebateResumed, handleDebateTurn,
4979
4979
  // --- Debate ---
4980
4980
  case "debate_preparing":
4981
4981
  showDebateSticky("preparing", msg);
4982
+ handleDebatePreparing(msg);
4982
4983
  break;
4983
4984
 
4984
4985
  case "debate_brief_ready":
@@ -5412,6 +5413,7 @@ import { initDebate, handleDebateStarted, handleDebateResumed, handleDebateTurn,
5412
5413
  matesList: function () { return cachedMatesList || []; },
5413
5414
  availableBuiltins: function () { return cachedAvailableBuiltins || []; },
5414
5415
  currentMateId: function () { return (dmTargetUser && dmTargetUser.isMate) ? dmTargetUser.id : null; },
5416
+ requireSkills: requireSkills,
5415
5417
  });
5416
5418
 
5417
5419
  // --- STT module (voice input via Web Speech API) ---
@@ -6167,30 +6169,15 @@ import { initDebate, handleDebateStarted, handleDebateResumed, handleDebateTurn,
6167
6169
  }, cb);
6168
6170
  }
6169
6171
 
6170
- function requireClayDebateSetup(cb) {
6171
- requireSkills({
6172
- title: "Skill Installation Required",
6173
- reason: "The Debate Setup skill is required to start a debate.",
6174
- skills: [{ name: "clay-debate-setup", url: "https://github.com/chadbyte/clay-debate-setup", scope: "global" }]
6175
- }, cb);
6176
- }
6177
6172
 
6178
- // Debate button in mate sidebar
6173
+ // Debate button in mate sidebar (only visible in DM mode)
6179
6174
  var debateBtn = document.getElementById("mate-debate-btn");
6180
6175
  if (debateBtn) {
6181
6176
  debateBtn.addEventListener("click", function () {
6182
- if (dmMode && dmTargetUser && dmTargetUser.isMate) {
6183
- // Quick debate: moderator is the current DM mate, uses conversation context
6184
- // Build messages with isMate flag for context extraction
6185
- var contextMessages = dmMessageCache.map(function (m) {
6186
- return { text: m.text, isMate: m.from !== myUserId };
6187
- });
6188
- openQuickDebateModal(contextMessages);
6189
- } else {
6190
- requireClayDebateSetup(function () {
6191
- openDebateModal();
6192
- });
6193
- }
6177
+ var contextMessages = dmMessageCache.map(function (m) {
6178
+ return { text: m.text, isMate: m.from !== myUserId };
6179
+ });
6180
+ openDebateModal({ dmContext: contextMessages });
6194
6181
  });
6195
6182
  }
6196
6183
 
@@ -331,6 +331,54 @@
331
331
  color: var(--text);
332
332
  }
333
333
 
334
+ /* --- Preparing indicator (shown while brief is being generated) --- */
335
+ .debate-preparing-indicator {
336
+ max-width: var(--content-width);
337
+ margin: 32px auto;
338
+ padding: 0 20px;
339
+ }
340
+
341
+ .debate-preparing-inner {
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 12px;
345
+ padding: 16px 20px;
346
+ border-radius: 12px;
347
+ background: color-mix(in srgb, var(--accent2) 8%, transparent);
348
+ border: 1px solid color-mix(in srgb, var(--accent2) 15%, transparent);
349
+ }
350
+
351
+ .debate-preparing-spinner {
352
+ display: inline-flex;
353
+ flex-shrink: 0;
354
+ color: var(--accent2);
355
+ animation: spin 1.2s linear infinite;
356
+ }
357
+
358
+ .debate-preparing-spinner .lucide {
359
+ width: 20px;
360
+ height: 20px;
361
+ }
362
+
363
+ .debate-preparing-text {
364
+ font-size: 14px;
365
+ color: var(--text-secondary);
366
+ line-height: 1.5;
367
+ }
368
+
369
+ .debate-preparing-dots {
370
+ animation: blink-dots 1.4s steps(4, end) infinite;
371
+ }
372
+
373
+ @keyframes spin {
374
+ to { transform: rotate(360deg); }
375
+ }
376
+
377
+ @keyframes blink-dots {
378
+ 0%, 20% { opacity: 0; }
379
+ 40% { opacity: 1; }
380
+ }
381
+
334
382
  /* --- Activity bar inside turns --- */
335
383
  .debate-activity-bar {
336
384
  margin: 4px 0;
@@ -862,6 +910,31 @@
862
910
  line-height: 1.4;
863
911
  }
864
912
 
913
+ .debate-skip-setup-row {
914
+ margin-top: 12px;
915
+ padding: 8px 0;
916
+ }
917
+
918
+ .debate-toggle-label {
919
+ display: flex;
920
+ align-items: center;
921
+ gap: 8px;
922
+ font-size: 13px;
923
+ color: var(--text-secondary);
924
+ cursor: pointer;
925
+ }
926
+
927
+ .debate-toggle-label input[type="checkbox"] {
928
+ margin: 0;
929
+ cursor: pointer;
930
+ }
931
+
932
+ .debate-skip-setup-hint {
933
+ font-size: 11px;
934
+ color: var(--text-muted);
935
+ margin-left: 4px;
936
+ }
937
+
865
938
  .debate-modal-footer {
866
939
  display: flex;
867
940
  justify-content: flex-end;
@@ -1433,6 +1433,13 @@
1433
1433
  <textarea id="debate-topic-input" rows="2" placeholder="e.g. Should AI development be regulated?"></textarea>
1434
1434
  <label class="debate-field-label" for="debate-panel-list">Select panelists</label>
1435
1435
  <div id="debate-panel-list" class="debate-panel-list"></div>
1436
+ <div id="debate-skip-setup-row" class="debate-skip-setup-row hidden">
1437
+ <label class="debate-toggle-label">
1438
+ <input type="checkbox" id="debate-skip-setup-toggle">
1439
+ <span>Skip setup</span>
1440
+ <span class="debate-skip-setup-hint">Moderator generates the brief automatically from conversation context</span>
1441
+ </label>
1442
+ </div>
1436
1443
  </div>
1437
1444
  <div class="debate-modal-footer">
1438
1445
  <button id="debate-modal-cancel" class="debate-btn-cancel">Cancel</button>
@@ -31,6 +31,11 @@ export function resetDebateState() {
31
31
  flushTurnStream();
32
32
  currentTurnEl = null;
33
33
  currentTurnMateId = null;
34
+ // Remove preparing indicator if present
35
+ if (ctx && ctx.messagesEl) {
36
+ var prep = ctx.messagesEl.querySelector(".debate-preparing-indicator");
37
+ if (prep) prep.remove();
38
+ }
34
39
  }
35
40
 
36
41
  function buildAvatarUrl(meta) {
@@ -85,7 +90,43 @@ export function handleDebateResumed(msg) {
85
90
  showDebateInfoFloat(msg);
86
91
  }
87
92
 
93
+ export function handleDebatePreparing(msg) {
94
+ debatePhase = "preparing";
95
+
96
+ if (!ctx.messagesEl) return;
97
+
98
+ // Remove any existing preparing indicator
99
+ var existing = ctx.messagesEl.querySelector(".debate-preparing-indicator");
100
+ if (existing) existing.remove();
101
+
102
+ var el = document.createElement("div");
103
+ el.className = "debate-preparing-indicator";
104
+
105
+ var moderatorName = msg.moderatorName || "Moderator";
106
+ var panelistNames = (msg.panelists || []).map(function (p) { return p.name; }).filter(Boolean).join(", ");
107
+
108
+ el.innerHTML =
109
+ '<div class="debate-preparing-inner">' +
110
+ '<div class="debate-preparing-spinner">' + iconHtml("loader") + '</div>' +
111
+ '<div class="debate-preparing-text">' +
112
+ '<strong>' + escapeHtml(moderatorName) + '</strong> is setting up the debate' +
113
+ (panelistNames ? ' with <strong>' + escapeHtml(panelistNames) + '</strong>' : '') +
114
+ '<span class="debate-preparing-dots">...</span>' +
115
+ '</div>' +
116
+ '</div>';
117
+
118
+ ctx.messagesEl.appendChild(el);
119
+ refreshIcons();
120
+ if (ctx.scrollToBottom) ctx.scrollToBottom();
121
+ }
122
+
88
123
  export function handleDebateStarted(msg) {
124
+ // Remove preparing indicator when debate goes live
125
+ if (ctx.messagesEl) {
126
+ var prep = ctx.messagesEl.querySelector(".debate-preparing-indicator");
127
+ if (prep) prep.remove();
128
+ }
129
+
89
130
  debateActive = true;
90
131
  debateTopic = msg.topic || "";
91
132
  debateRound = 1;
@@ -494,7 +535,8 @@ export function isDebateActive() {
494
535
  var modalEl = null;
495
536
  var selectedPanelists = [];
496
537
 
497
- export function openDebateModal() {
538
+ export function openDebateModal(opts) {
539
+ opts = opts || {};
498
540
  modalEl = document.getElementById("debate-modal");
499
541
  if (!modalEl) return;
500
542
 
@@ -506,6 +548,46 @@ export function openDebateModal() {
506
548
  topicInput.focus();
507
549
  }
508
550
 
551
+ // Show/hide skip-setup toggle (only when dmContext is available)
552
+ var skipSetupRow = document.getElementById("debate-skip-setup-row");
553
+ var skipSetupToggle = document.getElementById("debate-skip-setup-toggle");
554
+ if (skipSetupRow) {
555
+ if (opts.dmContext) {
556
+ skipSetupRow.classList.remove("hidden");
557
+ } else {
558
+ skipSetupRow.classList.add("hidden");
559
+ }
560
+ }
561
+ if (skipSetupToggle) {
562
+ skipSetupToggle.checked = false;
563
+ skipSetupToggle.onchange = function () {
564
+ var topicLabel = modalEl.querySelector('.debate-field-label[for="debate-topic-input"]');
565
+ var reqSpan = topicLabel ? topicLabel.querySelector(".debate-field-req") : null;
566
+ if (skipSetupToggle.checked) {
567
+ if (topicInput) topicInput.placeholder = "Leave blank to auto-detect from conversation...";
568
+ if (reqSpan) reqSpan.style.display = "none";
569
+ } else {
570
+ if (topicInput) topicInput.placeholder = "e.g. Should AI development be regulated?";
571
+ if (reqSpan) reqSpan.style.display = "";
572
+ }
573
+ };
574
+ }
575
+
576
+ // Build DM context string for quick start
577
+ var dmContextStr = "";
578
+ if (opts.dmContext && opts.dmContext.length) {
579
+ var recent = opts.dmContext.slice(-20);
580
+ var parts = [];
581
+ for (var i = 0; i < recent.length; i++) {
582
+ var m = recent[i];
583
+ var speaker = m.isMate ? "Mate" : "User";
584
+ var text = m.text || "";
585
+ if (text.length > 500) text = text.substring(0, 500) + "...";
586
+ parts.push(speaker + ": " + text);
587
+ }
588
+ dmContextStr = parts.join("\n");
589
+ }
590
+
509
591
  // Populate panelist list from mates (exclude current mate = moderator)
510
592
  var panelList = document.getElementById("debate-panel-list");
511
593
  if (panelList) {
@@ -543,7 +625,9 @@ export function openDebateModal() {
543
625
  if (startBtn) {
544
626
  startBtn.onclick = function () {
545
627
  var topic = topicInput ? topicInput.value.trim() : "";
546
- if (!topic) {
628
+ var skipSetup = skipSetupToggle && skipSetupToggle.checked;
629
+
630
+ if (!skipSetup && !topic) {
547
631
  topicInput.focus();
548
632
  return;
549
633
  }
@@ -552,32 +636,54 @@ export function openDebateModal() {
552
636
  var currentMateId = ctx.currentMateId ? ctx.currentMateId() : null;
553
637
  if (!currentMateId) return;
554
638
 
555
- // Create a new session first, then send debate_start after switch
556
- if (ctx.ws) {
557
- var debatePayload = {
558
- type: "debate_start",
559
- moderatorId: currentMateId,
560
- topic: topic,
561
- panelists: selectedPanelists.map(function (id) {
562
- return { mateId: id, role: "", brief: "" };
563
- }),
564
- };
565
-
566
- // Listen for session_switched once, then send debate_start
567
- var onMessage = function (evt) {
568
- try {
569
- var data = JSON.parse(evt.data);
570
- if (data.type === "session_switched") {
571
- ctx.ws.removeEventListener("message", onMessage);
572
- ctx.ws.send(JSON.stringify(debatePayload));
573
- }
574
- } catch (e) {}
575
- };
576
- ctx.ws.addEventListener("message", onMessage);
577
- ctx.ws.send(JSON.stringify({ type: "new_session" }));
639
+ var debatePayload = {
640
+ type: "debate_start",
641
+ moderatorId: currentMateId,
642
+ topic: topic || (skipSetup ? "(auto-detect from conversation)" : ""),
643
+ panelists: selectedPanelists.map(function (id) {
644
+ return { mateId: id, role: "", brief: "" };
645
+ }),
646
+ };
647
+
648
+ // When skip setup is toggled, send as quick start with DM context
649
+ if (skipSetup) {
650
+ debatePayload.quickStart = true;
651
+ debatePayload.dmContext = dmContextStr;
652
+ }
653
+
654
+ function sendDebate() {
655
+ // Create a new session first, then send debate_start after switch
656
+ if (ctx.ws) {
657
+ var onMessage = function (evt) {
658
+ try {
659
+ var data = JSON.parse(evt.data);
660
+ if (data.type === "session_switched") {
661
+ ctx.ws.removeEventListener("message", onMessage);
662
+ ctx.ws.send(JSON.stringify(debatePayload));
663
+ }
664
+ } catch (e) {}
665
+ };
666
+ ctx.ws.addEventListener("message", onMessage);
667
+ ctx.ws.send(JSON.stringify({ type: "new_session" }));
668
+ }
669
+ closeDebateModal();
578
670
  }
579
671
 
580
- closeDebateModal();
672
+ if (skipSetup) {
673
+ // Quick start: no skill needed
674
+ sendDebate();
675
+ } else {
676
+ // Standard: require clay-debate-setup skill first
677
+ if (ctx.requireSkills) {
678
+ ctx.requireSkills({
679
+ title: "Skill Installation Required",
680
+ reason: "The Debate Setup skill is required to start a debate.",
681
+ skills: [{ name: "clay-debate-setup", url: "https://github.com/chadbyte/clay-debate-setup", scope: "global" }]
682
+ }, sendDebate);
683
+ } else {
684
+ sendDebate();
685
+ }
686
+ }
581
687
  };
582
688
  }
583
689
 
@@ -655,6 +761,10 @@ export function closeDebateModal() {
655
761
  modalEl.classList.add("hidden");
656
762
  }
657
763
  selectedPanelists = [];
764
+ var skipSetupToggle = document.getElementById("debate-skip-setup-toggle");
765
+ if (skipSetupToggle) skipSetupToggle.checked = false;
766
+ var skipSetupRow = document.getElementById("debate-skip-setup-row");
767
+ if (skipSetupRow) skipSetupRow.classList.add("hidden");
658
768
  }
659
769
 
660
770
  // --- Quick Debate: start debate from DM context ---