clay-server 5.1.0-beta.1 → 5.1.0-beta.3

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 (231) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/bin/cli.js +9 -91
  4. package/lib/builtin-mates.js +7 -7
  5. package/lib/daemon-project-access.js +7 -1
  6. package/lib/daemon-settings.js +11 -0
  7. package/lib/daemon.js +10 -37
  8. package/lib/dicebear-avatar.js +62 -0
  9. package/lib/knowledge-db-service.js +175 -0
  10. package/lib/knowledge-db-validation.js +0 -0
  11. package/lib/knowledge-document-index.js +49 -0
  12. package/lib/knowledge-draft-store.js +56 -0
  13. package/lib/knowledge-file-store.js +199 -0
  14. package/lib/knowledge-markdown.js +128 -0
  15. package/lib/linear-store.js +35 -8
  16. package/lib/mate-identity-file.js +39 -0
  17. package/lib/mate-knowledge-mcp-server.js +29 -6
  18. package/lib/mate-knowledge-service.js +23 -2
  19. package/lib/mate-knowledge-sync.js +37 -22
  20. package/lib/mate-ready-creation.js +2 -1
  21. package/lib/mate-workspace-color.js +4 -0
  22. package/lib/mates-identity.js +4 -4
  23. package/lib/mates-prompts.js +4 -3
  24. package/lib/mates.js +30 -11
  25. package/lib/os-users.js +5 -16
  26. package/lib/project-connection.js +36 -9
  27. package/lib/project-debate-proposal.js +6 -0
  28. package/lib/project-debate-workspace.js +43 -0
  29. package/lib/project-debate.js +4 -2
  30. package/lib/project-filesystem.js +21 -39
  31. package/lib/project-knowledge.js +315 -136
  32. package/lib/project-mate-creation-proposal.js +1 -0
  33. package/lib/project-mate-interaction.js +7 -6
  34. package/lib/project-server-settings.js +31 -0
  35. package/lib/project-sessions.js +92 -182
  36. package/lib/project-user-knowledge.js +60 -0
  37. package/lib/project.js +62 -21
  38. package/lib/public/app.js +79 -71
  39. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  40. package/lib/public/css/clay-workspace.css +105 -0
  41. package/lib/public/css/debates-workbench.css +47 -0
  42. package/lib/public/css/home-debate-live.css +26 -0
  43. package/lib/public/css/home-hub.css +3 -1
  44. package/lib/public/css/home-mate-model-picker.css +15 -131
  45. package/lib/public/css/home-mate-settings.css +168 -16
  46. package/lib/public/css/icon-strip.css +249 -32
  47. package/lib/public/css/input.css +3 -1
  48. package/lib/public/css/mate-conversation.css +176 -0
  49. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  50. package/lib/public/css/mate-workspace-color.css +16 -0
  51. package/lib/public/css/mates.css +28 -28
  52. package/lib/public/css/menus.css +3 -0
  53. package/lib/public/css/messages.css +1 -114
  54. package/lib/public/css/mobile-nav.css +16 -16
  55. package/lib/public/css/overlays.css +15 -0
  56. package/lib/public/css/project-logs.css +7 -0
  57. package/lib/public/css/project-mate-creation.css +105 -0
  58. package/lib/public/css/project-settings-modal.css +56 -0
  59. package/lib/public/css/right-workbench.css +3 -3
  60. package/lib/public/css/server-settings-system.css +125 -0
  61. package/lib/public/css/session-folders.css +80 -21
  62. package/lib/public/css/session-github.css +25 -16
  63. package/lib/public/css/settings-ui.css +247 -22
  64. package/lib/public/css/shared-browser.css +7 -3
  65. package/lib/public/css/sidebar.css +183 -107
  66. package/lib/public/css/title-bar.css +156 -1
  67. package/lib/public/css/worker-delegation.css +47 -0
  68. package/lib/public/css/worker-proposal.css +72 -150
  69. package/lib/public/css/you.css +74 -0
  70. package/lib/public/index.html +206 -548
  71. package/lib/public/modules/account-island.js +232 -0
  72. package/lib/public/modules/admin-settings-email.js +197 -0
  73. package/lib/public/modules/admin-settings-invitations.js +183 -0
  74. package/lib/public/modules/admin-settings-projects.js +198 -0
  75. package/lib/public/modules/admin-settings-ui.js +100 -0
  76. package/lib/public/modules/admin-settings-users.js +331 -0
  77. package/lib/public/modules/admin.js +19 -949
  78. package/lib/public/modules/app-cursors.js +19 -43
  79. package/lib/public/modules/app-debate-ui.js +4 -3
  80. package/lib/public/modules/app-dm.js +0 -2
  81. package/lib/public/modules/app-header.js +106 -0
  82. package/lib/public/modules/app-message-router.js +12 -3
  83. package/lib/public/modules/app-messages.js +47 -61
  84. package/lib/public/modules/app-notifications.js +4 -7
  85. package/lib/public/modules/app-projects.js +5 -38
  86. package/lib/public/modules/app-rate-limit.js +70 -12
  87. package/lib/public/modules/app-rendering.js +12 -13
  88. package/lib/public/modules/avatar.js +30 -9
  89. package/lib/public/modules/chat-render-runtime.js +16 -4
  90. package/lib/public/modules/command-palette.js +2 -15
  91. package/lib/public/modules/context-sources.js +27 -18
  92. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  93. package/lib/public/modules/debate-controls.js +299 -0
  94. package/lib/public/modules/debate.js +2 -0
  95. package/lib/public/modules/debates-workbench.js +339 -0
  96. package/lib/public/modules/default-ai.js +88 -65
  97. package/lib/public/modules/header-tui-font.js +10 -20
  98. package/lib/public/modules/home-debate-controls.js +18 -289
  99. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  100. package/lib/public/modules/home-mate-color-picker.js +66 -0
  101. package/lib/public/modules/home-mate-creation.js +1 -1
  102. package/lib/public/modules/home-mate-instructions.js +90 -0
  103. package/lib/public/modules/home-mate-model-picker.js +80 -271
  104. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  105. package/lib/public/modules/home-mate-settings.js +118 -125
  106. package/lib/public/modules/home-shell.js +3 -0
  107. package/lib/public/modules/home-surface-boot.js +1 -9
  108. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  109. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  110. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  111. package/lib/public/modules/knowledge-history-state.js +55 -0
  112. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  113. package/lib/public/modules/knowledge-preview-links.js +70 -0
  114. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  115. package/lib/public/modules/linear-settings.js +3 -2
  116. package/lib/public/modules/markdown.js +5 -6
  117. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  118. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  119. package/lib/public/modules/mate-knowledge.js +11 -3
  120. package/lib/public/modules/mate-management.js +54 -10
  121. package/lib/public/modules/mate-sidebar.js +11 -1
  122. package/lib/public/modules/mate-workspace-color.js +33 -0
  123. package/lib/public/modules/mention.js +5 -13
  124. package/lib/public/modules/notification-settings.js +81 -0
  125. package/lib/public/modules/notifications.js +44 -28
  126. package/lib/public/modules/personal-settings-dialog.js +51 -0
  127. package/lib/public/modules/profile.js +66 -24
  128. package/lib/public/modules/project-chat-identity.js +79 -0
  129. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  130. package/lib/public/modules/project-debate-workspace.js +185 -0
  131. package/lib/public/modules/project-header-identity.js +127 -0
  132. package/lib/public/modules/project-logs-render.js +9 -4
  133. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  134. package/lib/public/modules/project-mate-navigation.js +238 -0
  135. package/lib/public/modules/project-session-navigation.js +29 -0
  136. package/lib/public/modules/project-settings-environment.js +106 -0
  137. package/lib/public/modules/project-settings.js +72 -628
  138. package/lib/public/modules/project-switcher.js +6 -8
  139. package/lib/public/modules/projects-hub-view.js +5 -6
  140. package/lib/public/modules/projects-hub.js +2 -6
  141. package/lib/public/modules/qrcode.js +27 -8
  142. package/lib/public/modules/search-clay-chat.js +8 -11
  143. package/lib/public/modules/server-settings-controls.js +316 -0
  144. package/lib/public/modules/server-settings-environment.js +194 -0
  145. package/lib/public/modules/server-settings-navigation.js +39 -0
  146. package/lib/public/modules/server-settings-state.js +54 -0
  147. package/lib/public/modules/server-settings.js +116 -788
  148. package/lib/public/modules/server-storage-settings.js +84 -0
  149. package/lib/public/modules/session-activity-tooltip.js +12 -0
  150. package/lib/public/modules/session-create-form.js +86 -14
  151. package/lib/public/modules/session-create.js +50 -7
  152. package/lib/public/modules/session-github.js +2 -1
  153. package/lib/public/modules/session-list-visibility.js +41 -0
  154. package/lib/public/modules/session-presence-ui.js +3 -0
  155. package/lib/public/modules/session-title-marquee.js +51 -0
  156. package/lib/public/modules/settings-env-values.js +34 -0
  157. package/lib/public/modules/settings-select.js +121 -0
  158. package/lib/public/modules/shared-browser.js +22 -12
  159. package/lib/public/modules/sidebar-mates.js +3 -50
  160. package/lib/public/modules/sidebar-mobile.js +63 -60
  161. package/lib/public/modules/sidebar-projects.js +84 -97
  162. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  163. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  164. package/lib/public/modules/sidebar-sessions.js +49 -50
  165. package/lib/public/modules/sidebar.js +11 -13
  166. package/lib/public/modules/stt-picker.js +14 -5
  167. package/lib/public/modules/terminal-prefs.js +22 -0
  168. package/lib/public/modules/theme.js +88 -52
  169. package/lib/public/modules/thinking-lifecycle.js +7 -6
  170. package/lib/public/modules/tool-palette-order.js +4 -3
  171. package/lib/public/modules/tool-palette.js +13 -3
  172. package/lib/public/modules/tools.js +22 -22
  173. package/lib/public/modules/user-settings-account.js +318 -0
  174. package/lib/public/modules/user-settings-appearance.js +55 -0
  175. package/lib/public/modules/user-settings-editors.js +24 -0
  176. package/lib/public/modules/user-settings-integrations.js +104 -0
  177. package/lib/public/modules/user-settings-layout.js +80 -0
  178. package/lib/public/modules/user-settings-preferences.js +48 -0
  179. package/lib/public/modules/user-settings-profile.js +53 -0
  180. package/lib/public/modules/user-settings-themes.js +38 -0
  181. package/lib/public/modules/user-settings-voice.js +75 -0
  182. package/lib/public/modules/user-settings.js +53 -480
  183. package/lib/public/modules/worker-proposal.js +42 -8
  184. package/lib/public/modules/worker-runtime-picker.js +115 -0
  185. package/lib/public/modules/you-activity.js +121 -0
  186. package/lib/public/modules/you.js +251 -0
  187. package/lib/public/style.css +20 -8
  188. package/lib/public/vendor/codemirror/LICENSE +21 -0
  189. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  190. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  191. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  192. package/lib/public/vendor/codemirror/markdown.js +886 -0
  193. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  194. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  195. package/lib/sdk-bridge.js +15 -3
  196. package/lib/sdk-message-processor.js +16 -4
  197. package/lib/sdk-quota-snapshot.js +37 -0
  198. package/lib/server-admin.js +11 -0
  199. package/lib/server-appearance-settings.js +71 -0
  200. package/lib/server-home-chat-events.js +1 -1
  201. package/lib/server-home-chat.js +25 -3
  202. package/lib/server-home-debate-planning.js +4 -1
  203. package/lib/server-home-debates.js +2 -0
  204. package/lib/server-home-mate-creation.js +4 -1
  205. package/lib/server-linear.js +14 -8
  206. package/lib/server-mate-instructions.js +44 -0
  207. package/lib/server-mates.js +14 -4
  208. package/lib/server-notification-settings.js +67 -0
  209. package/lib/server-palette.js +1 -1
  210. package/lib/server-settings.js +69 -1
  211. package/lib/server-storage.js +57 -0
  212. package/lib/server-user-knowledge.js +119 -0
  213. package/lib/server.js +31 -3
  214. package/lib/session-visibility.js +7 -2
  215. package/lib/sessions.js +35 -10
  216. package/lib/startup-projects.js +40 -0
  217. package/lib/storage-native-walk.js +85 -0
  218. package/lib/storage-usage.js +144 -0
  219. package/lib/user-knowledge-service.js +167 -0
  220. package/lib/users-permissions.js +7 -0
  221. package/lib/users-preferences.js +0 -74
  222. package/lib/users-project-mate-filter-preferences.js +39 -0
  223. package/lib/users.js +6 -4
  224. package/lib/workspace-query-access.js +4 -1
  225. package/lib/ws-schema.js +52 -4
  226. package/lib/yoke/acp-process-manager.js +2 -2
  227. package/lib/yoke/adapters/codex.js +12 -52
  228. package/lib/yoke/codex-app-server.js +2 -2
  229. package/lib/yoke/codex-model-catalog.js +52 -0
  230. package/lib/yoke/codex-rate-limits.js +26 -0
  231. package/package.json +12 -3
@@ -0,0 +1,299 @@
1
+ // Shared debate participation controls for Home and project workspaces.
2
+
3
+ function findHeader(messages) {
4
+ for (var i = (messages || []).length - 1; i >= 0; i--) if (messages[i] && messages[i].role === "debate_header") return messages[i];
5
+ return null;
6
+ }
7
+
8
+ function findActiveTurn(messages) {
9
+ for (var i = (messages || []).length - 1; i >= 0; i--) {
10
+ if (messages[i] && messages[i].role === "debate_turn" && messages[i].status === "active") return messages[i];
11
+ }
12
+ return null;
13
+ }
14
+
15
+ export function homeDebateControlState(messages) {
16
+ var header = findHeader(messages);
17
+ if (!header || ["live", "ended", "interrupted"].indexOf(header.phase) === -1) return null;
18
+ var mode = header.phase === "live" ? (header.interaction || "default") : "terminal";
19
+ return { mode: mode, header: header, turn: findActiveTurn(messages), phase: header.phase };
20
+ }
21
+
22
+ export function createDebateControls(options) {
23
+ var slotEl = null;
24
+ var normalComposerEl = null;
25
+ var modelEl = null;
26
+ var controlEl = null;
27
+ var controlMode = null;
28
+ var controlStateKey = null;
29
+ var currentState = null;
30
+ var currentRequestId = null;
31
+ var startNewDebate = null;
32
+ var inputEl = null;
33
+ var primaryEl = null;
34
+ var secondaryEl = null;
35
+ var activityEl = null;
36
+ var handEl = null;
37
+ var stopEl = null;
38
+ var composing = false;
39
+ var sizeAnimation = null;
40
+
41
+ function textElement(tag, className, text) {
42
+ var element = document.createElement(tag);
43
+ element.className = className;
44
+ element.textContent = text || "";
45
+ return element;
46
+ }
47
+
48
+ function ensureElements() {
49
+ var nextSlot = document.getElementById(options.slotId);
50
+ if (slotEl === nextSlot) return !!slotEl;
51
+ slotEl = nextSlot;
52
+ normalComposerEl = document.getElementById(options.composerId);
53
+ modelEl = options.modelId ? document.getElementById(options.modelId) : null;
54
+ controlEl = null;
55
+ controlMode = null;
56
+ controlStateKey = null;
57
+ return !!slotEl;
58
+ }
59
+
60
+ function clearControls() {
61
+ if (sizeAnimation) { sizeAnimation.cancel(); sizeAnimation = null; }
62
+ if (slotEl) slotEl.innerHTML = "";
63
+ controlEl = null;
64
+ controlMode = null;
65
+ controlStateKey = null;
66
+ currentState = null;
67
+ inputEl = null;
68
+ primaryEl = null;
69
+ secondaryEl = null;
70
+ activityEl = null;
71
+ handEl = null;
72
+ stopEl = null;
73
+ composing = false;
74
+ }
75
+
76
+ function setSpecialMode(active) {
77
+ if (normalComposerEl) normalComposerEl.hidden = active;
78
+ if (modelEl) modelEl.hidden = active;
79
+ if (slotEl) slotEl.hidden = !active;
80
+ }
81
+
82
+ function sendControl(action, data) {
83
+ if (!currentState) return false;
84
+ return options.send(action, data || {}, currentRequestId);
85
+ }
86
+
87
+ function button(label, className) {
88
+ var element = textElement("button", className, label);
89
+ element.type = "button";
90
+ return element;
91
+ }
92
+
93
+ function dots() {
94
+ var group = document.createElement("span");
95
+ group.className = "home-debate-control-dots";
96
+ group.setAttribute("aria-hidden", "true");
97
+ for (var i = 0; i < 3; i++) group.appendChild(document.createElement("i"));
98
+ return group;
99
+ }
100
+
101
+ function activityLabel(state) {
102
+ if (state.header.stopping) return state.turn ? "Stopping after " + (state.turn.mateName || "the current speaker") + " finishes" : "Stopping debate";
103
+ if (!state.turn) return state.header.handRaised ? "Your hand is raised" : "Debate is live";
104
+ var name = state.turn.mateName || "Mate";
105
+ var activity = state.turn.activity || (state.turn.text ? "speaking" : "preparing");
106
+ return name + " is " + activity.toLowerCase();
107
+ }
108
+
109
+ function lockSubmission(label) {
110
+ if (inputEl) inputEl.disabled = true;
111
+ if (primaryEl) { primaryEl.disabled = true; primaryEl.textContent = label; }
112
+ if (secondaryEl) secondaryEl.disabled = true;
113
+ }
114
+
115
+ function submitInput(action, response) {
116
+ var text = inputEl ? inputEl.value.trim() : "";
117
+ if (action === "user_floor" && !text) return false;
118
+ if (!sendControl(action, { text: text, response: response })) return false;
119
+ lockSubmission(action === "user_floor" ? "Sending…" : "Continuing…");
120
+ return true;
121
+ }
122
+
123
+ function bindInput(submit) {
124
+ inputEl.addEventListener("compositionstart", function () { composing = true; });
125
+ inputEl.addEventListener("compositionend", function () { composing = false; });
126
+ inputEl.addEventListener("input", function () {
127
+ if (controlMode === "user_floor" && primaryEl) primaryEl.disabled = !inputEl.value.trim();
128
+ });
129
+ inputEl.addEventListener("keydown", function (event) {
130
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing || composing || event.keyCode === 229) return;
131
+ event.preventDefault();
132
+ submit();
133
+ });
134
+ }
135
+
136
+ function focusInputOnce() {
137
+ var target = inputEl;
138
+ if (!target) return;
139
+ var focus = function () {
140
+ if (target === inputEl && !target.disabled && target.isConnected !== false) target.focus({ preventScroll: true });
141
+ };
142
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(focus);
143
+ else focus();
144
+ }
145
+
146
+ function buildDefault(state) {
147
+ var context = document.createElement("div");
148
+ context.className = "home-debate-control-context";
149
+ context.appendChild(dots());
150
+ activityEl = textElement("span", "home-debate-control-activity", activityLabel(state));
151
+ activityEl.setAttribute("role", "status");
152
+ activityEl.setAttribute("aria-live", "polite");
153
+ context.appendChild(activityEl);
154
+ controlEl.appendChild(context);
155
+ var actions = document.createElement("div");
156
+ actions.className = "home-debate-control-actions";
157
+ handEl = button(state.header.handRaised ? "Hand raised" : "Raise hand", "home-debate-control-secondary");
158
+ handEl.setAttribute("aria-pressed", state.header.handRaised ? "true" : "false");
159
+ handEl.disabled = state.header.handRaised === true;
160
+ handEl.addEventListener("click", function () {
161
+ if (!sendControl("hand_raise")) return;
162
+ handEl.disabled = true;
163
+ handEl.textContent = "Raising hand…";
164
+ });
165
+ stopEl = button(state.header.stopping ? "Cancel stop" : "Stop debate", state.header.stopping ? "home-debate-control-secondary" : "home-debate-control-stop");
166
+ stopEl.addEventListener("click", function () {
167
+ var cancelling = currentState && currentState.header && currentState.header.stopping === true;
168
+ if (!sendControl(cancelling ? "cancel_stop" : "stop")) return;
169
+ stopEl.disabled = true;
170
+ stopEl.textContent = cancelling ? "Cancelling…" : "Stopping…";
171
+ });
172
+ actions.appendChild(handEl);
173
+ actions.appendChild(stopEl);
174
+ controlEl.appendChild(actions);
175
+ }
176
+
177
+ function buildInputMode(state) {
178
+ var isFloor = state.mode === "user_floor";
179
+ var heading = textElement("div", "home-debate-control-heading", isFloor ? "You have the floor" : "The moderator is ready to conclude");
180
+ heading.id = options.slotId + "-heading";
181
+ controlEl.appendChild(heading);
182
+ inputEl = document.createElement("textarea");
183
+ inputEl.rows = 2;
184
+ inputEl.placeholder = isFloor ? "Share your thoughts with the panel" : "Add an optional direction to continue";
185
+ inputEl.setAttribute("aria-labelledby", heading.id);
186
+ controlEl.appendChild(inputEl);
187
+ var actions = document.createElement("div");
188
+ actions.className = "home-debate-control-actions";
189
+ if (isFloor) {
190
+ primaryEl = button("Send", "home-debate-control-primary");
191
+ primaryEl.disabled = true;
192
+ primaryEl.addEventListener("click", function () { submitInput("user_floor"); });
193
+ secondaryEl = button("Pass", "home-debate-control-secondary");
194
+ secondaryEl.addEventListener("click", function () {
195
+ if (!sendControl("user_floor", { text: "(The user passed without speaking)" })) return;
196
+ lockSubmission("Sending…");
197
+ });
198
+ } else {
199
+ primaryEl = button("Continue", "home-debate-control-primary");
200
+ primaryEl.addEventListener("click", function () { submitInput("conclude", "continue"); });
201
+ secondaryEl = button("End debate", "home-debate-control-stop");
202
+ secondaryEl.addEventListener("click", function () {
203
+ if (!sendControl("conclude", { response: "end" })) return;
204
+ lockSubmission("Ending…");
205
+ });
206
+ }
207
+ actions.appendChild(primaryEl);
208
+ actions.appendChild(secondaryEl);
209
+ controlEl.appendChild(actions);
210
+ bindInput(function () { submitInput(isFloor ? "user_floor" : "conclude", isFloor ? null : "continue"); });
211
+ focusInputOnce();
212
+ }
213
+
214
+ function terminalLabel(state) {
215
+ if (state.phase === "interrupted") return "Debate interrupted when Clay restarted";
216
+ if (state.header.reason === "error") return "The debate stopped after an error";
217
+ if (state.header.reason === "stopped" || state.header.reason === "user_stopped") return "Debate stopped";
218
+ return "Debate ended";
219
+ }
220
+
221
+ function buildTerminal(state) {
222
+ var status = textElement("div", "home-debate-control-terminal-status", terminalLabel(state));
223
+ status.setAttribute("role", "status");
224
+ controlEl.appendChild(status);
225
+ var actions = document.createElement("div");
226
+ actions.className = "home-debate-control-actions";
227
+ primaryEl = button("Resume debate", "home-debate-control-primary");
228
+ primaryEl.addEventListener("click", function () {
229
+ if (!sendControl("resume")) return;
230
+ lockSubmission("Resuming…");
231
+ });
232
+ actions.appendChild(primaryEl);
233
+ if (typeof startNewDebate === "function") {
234
+ secondaryEl = button("New debate", "home-debate-control-secondary");
235
+ secondaryEl.addEventListener("click", function () { startNewDebate(); });
236
+ actions.appendChild(secondaryEl);
237
+ }
238
+ controlEl.appendChild(actions);
239
+ }
240
+
241
+ function buildControls(state) {
242
+ var previousHeight = slotEl.getBoundingClientRect ? slotEl.getBoundingClientRect().height : 0;
243
+ if (sizeAnimation) { sizeAnimation.cancel(); sizeAnimation = null; }
244
+ slotEl.innerHTML = "";
245
+ controlEl = document.createElement("section");
246
+ controlEl.className = "home-debate-control-surface home-debate-control-" + state.mode;
247
+ controlEl.setAttribute("role", state.mode === "default" ? "toolbar" : "region");
248
+ controlEl.setAttribute("aria-label", state.mode === "terminal" ? "Debate status" : "Live debate controls");
249
+ slotEl.appendChild(controlEl);
250
+ if (state.mode === "default") buildDefault(state);
251
+ else if (state.mode === "user_floor" || state.mode === "conclude") buildInputMode(state);
252
+ else buildTerminal(state);
253
+ if (previousHeight && slotEl.animate && !(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches)) {
254
+ var nextHeight = slotEl.getBoundingClientRect().height;
255
+ if (Math.abs(previousHeight - nextHeight) > 1) sizeAnimation = slotEl.animate([
256
+ { height: previousHeight + 'px' }, { height: nextHeight + 'px' }
257
+ ], { duration: 170, easing: 'ease-out' });
258
+ }
259
+ controlMode = state.mode;
260
+ controlStateKey = state.mode + ":" + state.phase + ":" + (state.header.reason || "");
261
+ }
262
+
263
+ function updateControls(state) {
264
+ if (state.mode !== "default") return;
265
+ var label = activityLabel(state);
266
+ if (activityEl && activityEl.textContent !== label) activityEl.textContent = label;
267
+ if (state.header.handRaised && handEl) {
268
+ handEl.textContent = "Hand raised";
269
+ handEl.disabled = true;
270
+ handEl.setAttribute("aria-pressed", "true");
271
+ }
272
+ if (stopEl) {
273
+ stopEl.disabled = false;
274
+ stopEl.textContent = state.header.stopping ? "Cancel stop" : "Stop debate";
275
+ stopEl.className = state.header.stopping ? "home-debate-control-secondary" : "home-debate-control-stop";
276
+ }
277
+ }
278
+
279
+ function renderControls(messages, requestId, onStartNewDebate) {
280
+ if (!ensureElements()) return false;
281
+ var state = homeDebateControlState(messages);
282
+ if (currentRequestId !== (requestId || null)) clearControls();
283
+ currentRequestId = requestId || null;
284
+ startNewDebate = onStartNewDebate || null;
285
+ if (!state) {
286
+ setSpecialMode(false);
287
+ clearControls();
288
+ return false;
289
+ }
290
+ currentState = state;
291
+ setSpecialMode(true);
292
+ var nextStateKey = state.mode + ":" + state.phase + ":" + (state.header.reason || "");
293
+ if (!controlEl || controlMode !== state.mode || controlStateKey !== nextStateKey) buildControls(state);
294
+ else updateControls(state);
295
+ return true;
296
+ }
297
+
298
+ return { render: renderControls };
299
+ }
@@ -1,3 +1,4 @@
1
+ import { resetProjectDebateWorkspace } from './project-debate-workspace.js';
1
2
  import { mateAvatarUrl } from './avatar.js';
2
3
  import { renderMarkdown, highlightCodeBlocks, buildPrintHtml, getPrintCss } from './markdown.js';
3
4
  import { escapeHtml } from './utils.js';
@@ -37,6 +38,7 @@ export function initDebate(_ctx) {
37
38
  }
38
39
 
39
40
  export function resetDebateState() {
41
+ resetProjectDebateWorkspace();
40
42
  debateActive = false;
41
43
  debateTopic = "";
42
44
  debateRound = 0;
@@ -0,0 +1,339 @@
1
+ // Cross-Mate debate archive and launcher. Conversation content remains in the
2
+ // owning Mate project; this panel is only an authorized index and launch pad.
3
+
4
+ import { store } from './store.js';
5
+ import { getWs } from './ws-ref.js';
6
+ import { iconHtml, refreshIcons } from './icons.js';
7
+ import { switchProject } from './app-projects.js';
8
+ import { claimRightWorkbench, registerRightWorkbench, releaseRightWorkbench } from './right-workbench.js';
9
+ import { startProjectDebatePlanning } from './project-debate-planning-workspace.js';
10
+ import { isMateWorkspace } from './project-mate-navigation.js';
11
+
12
+ var panel = null;
13
+ var requestSequence = 0;
14
+ var opener = null;
15
+ var backgroundState = [];
16
+
17
+ function narrowLayout() {
18
+ return typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(max-width: 1023px)').matches;
19
+ }
20
+
21
+ function isMateDebateSurface(state) {
22
+ return isMateWorkspace(state) || (state.dmMode === true && state.dmTargetUser && state.dmTargetUser.isMate === true);
23
+ }
24
+
25
+ function setNarrowBackground(hidden) {
26
+ var selectors = ['#app', '#sidebar-column', '#icon-strip', '#top-bar', '#main-column > .title-bar-content', '#dm-header-bar'];
27
+ if (hidden) {
28
+ backgroundState = [];
29
+ for (var i = 0; i < selectors.length; i++) {
30
+ var element = document.querySelector(selectors[i]);
31
+ if (!element) continue;
32
+ backgroundState.push({ element: element, inert: element.inert === true, ariaHidden: element.getAttribute('aria-hidden') });
33
+ element.inert = true;
34
+ element.setAttribute('aria-hidden', 'true');
35
+ }
36
+ return;
37
+ }
38
+ for (var j = 0; j < backgroundState.length; j++) {
39
+ backgroundState[j].element.inert = backgroundState[j].inert;
40
+ if (backgroundState[j].ariaHidden == null) backgroundState[j].element.removeAttribute('aria-hidden');
41
+ else backgroundState[j].element.setAttribute('aria-hidden', backgroundState[j].ariaHidden);
42
+ }
43
+ backgroundState = [];
44
+ }
45
+
46
+ function send(message) {
47
+ var ws = getWs();
48
+ if (!ws || ws.readyState !== 1) return false;
49
+ ws.send(JSON.stringify(message));
50
+ return true;
51
+ }
52
+
53
+ function phaseLabel(phase) {
54
+ if (phase === 'live') return 'Live';
55
+ if (phase === 'ended') return 'Ended';
56
+ if (phase === 'interrupted') return 'Interrupted';
57
+ return 'Planning';
58
+ }
59
+
60
+ function dateLabel(value) {
61
+ if (typeof value !== 'number' || !isFinite(value) || value <= 0) return 'Date unavailable';
62
+ try { return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(new Date(value)); }
63
+ catch (error) { return new Date(value).toLocaleDateString(); }
64
+ }
65
+
66
+ function participantsLabel(participants) {
67
+ var source = Array.isArray(participants) ? participants : [];
68
+ var names = [];
69
+ for (var i = 0; i < source.length; i++) if (source[i] && source[i].name) names.push(source[i].name);
70
+ return names.length ? names.join(', ') : 'Panel not selected';
71
+ }
72
+
73
+ function ensurePanel() {
74
+ if (panel) return panel;
75
+ var host = document.getElementById('main-panels');
76
+ if (!host) return null;
77
+ panel = document.createElement('section');
78
+ panel.id = 'debates-workbench';
79
+ panel.className = 'hidden';
80
+ panel.setAttribute('role', 'region');
81
+ panel.setAttribute('aria-label', 'Debates');
82
+ panel.innerHTML =
83
+ '<header class="debates-workbench-topbar">' +
84
+ '<span class="debates-workbench-title">' + iconHtml('messages-square') + 'Debates</span>' +
85
+ '<button class="debates-workbench-new" type="button" data-debates-action="new">' + iconHtml('plus') + 'New debate</button>' +
86
+ '<div class="debates-workbench-actions">' +
87
+ '<button class="debates-workbench-icon" type="button" data-debates-action="wide" title="Widen panel" aria-label="Widen Debates panel" aria-pressed="false">' + iconHtml('chevrons-left-right') + '</button>' +
88
+ '<button class="debates-workbench-icon" type="button" data-debates-action="fullscreen" title="Toggle fullscreen" aria-label="Toggle Debates fullscreen" aria-pressed="false">' + iconHtml('maximize-2') + '</button>' +
89
+ '<button class="debates-workbench-icon" type="button" data-debates-action="close" title="Close" aria-label="Close Debates">' + iconHtml('x') + '</button>' +
90
+ '</div>' +
91
+ '</header>' +
92
+ '<div class="debates-workbench-filters" role="group" aria-label="Filter debates"></div>' +
93
+ '<form class="debates-workbench-form hidden"><label for="debates-workbench-topic">What should the debate be about?</label><textarea id="debates-workbench-topic" maxlength="1000" required placeholder="Enter a question or topic"></textarea><p class="debates-workbench-launch-status" role="alert"></p><div class="debates-workbench-form-actions"><button type="button" data-debates-action="cancel-new">Cancel</button><button type="submit">Continue in chat</button></div></form>' +
94
+ '<p class="debates-workbench-summary" role="status" aria-live="polite"></p>' +
95
+ '<div class="debates-workbench-list" role="list"></div>';
96
+ host.appendChild(panel);
97
+ panel.addEventListener('click', handleClick);
98
+ panel.addEventListener('keydown', handleKeydown);
99
+ panel.querySelector('form').addEventListener('submit', submitNewDebate);
100
+ refreshIcons();
101
+ return panel;
102
+ }
103
+
104
+ function ensureMateProjectDebatesButton() {
105
+ var button = document.getElementById('mate-project-debates-btn');
106
+ if (button) return button;
107
+ var tools = document.getElementById('session-actions');
108
+ if (!tools) return null;
109
+ button = document.createElement('button');
110
+ button.id = 'mate-project-debates-btn';
111
+ button.type = 'button';
112
+ button.className = 'palette-tile';
113
+ button.title = 'Debates';
114
+ button.setAttribute('aria-label', 'Debates');
115
+ button.innerHTML = iconHtml('messages-square') + '<span class="tool-btn-label">Debates</span>';
116
+ tools.appendChild(button);
117
+ refreshIcons(button);
118
+ return button;
119
+ }
120
+
121
+ function requestDebates() {
122
+ requestSequence += 1;
123
+ var requestId = 'debates-workbench-' + requestSequence;
124
+ store.set({ debatesWorkbenchStatus: 'loading', debatesWorkbenchRequestId: requestId, debatesWorkbenchError: '' });
125
+ if (!send({ type: 'home_debates_list', requestId: requestId })) {
126
+ store.set({ debatesWorkbenchStatus: 'error', debatesWorkbenchError: 'Reconnect to load your debates.' });
127
+ render();
128
+ return;
129
+ }
130
+ render();
131
+ }
132
+
133
+ function renderFilters() {
134
+ var filters = panel.querySelector('.debates-workbench-filters');
135
+ var active = store.get('debatesWorkbenchFilter') || 'all';
136
+ var specs = [{ key: 'all', label: 'All' }, { key: 'planning', label: 'Planning' }, { key: 'live', label: 'Live' }, { key: 'ended', label: 'Ended' }, { key: 'interrupted', label: 'Interrupted' }];
137
+ filters.innerHTML = '';
138
+ for (var i = 0; i < specs.length; i++) {
139
+ var button = document.createElement('button');
140
+ button.type = 'button';
141
+ button.className = 'debates-workbench-filter';
142
+ button.dataset.debatesFilter = specs[i].key;
143
+ button.setAttribute('aria-pressed', specs[i].key === active ? 'true' : 'false');
144
+ button.textContent = specs[i].label;
145
+ filters.appendChild(button);
146
+ }
147
+ }
148
+
149
+ function debateRow(debate) {
150
+ var row = document.createElement('button');
151
+ row.type = 'button';
152
+ row.className = 'debates-workbench-row';
153
+ row.dataset.projectSlug = debate.projectSlug || '';
154
+ row.dataset.sessionId = String(debate.localId == null ? '' : debate.localId);
155
+ row.setAttribute('role', 'listitem');
156
+ var title = document.createElement('strong');
157
+ title.textContent = debate.topic || debate.title || 'Debate planning';
158
+ var panelists = document.createElement('span');
159
+ panelists.className = 'debates-workbench-row-panel';
160
+ panelists.textContent = participantsLabel(debate.participants);
161
+ var meta = document.createElement('span');
162
+ meta.className = 'debates-workbench-row-meta';
163
+ var phase = document.createElement('span');
164
+ phase.className = 'debates-workbench-phase';
165
+ phase.textContent = phaseLabel(debate.phase);
166
+ var date = document.createElement('span');
167
+ date.textContent = dateLabel(debate.lastActivity);
168
+ meta.appendChild(phase);
169
+ meta.appendChild(date);
170
+ row.appendChild(title);
171
+ row.appendChild(panelists);
172
+ row.appendChild(meta);
173
+ return row;
174
+ }
175
+
176
+ function render() {
177
+ if (!panel || panel.classList.contains('hidden')) return;
178
+ renderFilters();
179
+ panel.querySelector('form').classList.toggle('hidden', !store.get('debatesWorkbenchCreating'));
180
+ var summary = panel.querySelector('.debates-workbench-summary');
181
+ var list = panel.querySelector('.debates-workbench-list');
182
+ var status = store.get('debatesWorkbenchStatus') || 'idle';
183
+ var filter = store.get('debatesWorkbenchFilter') || 'all';
184
+ var debates = (store.get('debatesWorkbenchItems') || []).filter(function (item) { return filter === 'all' || item.phase === filter; });
185
+ list.innerHTML = '';
186
+ if (status === 'loading' || status === 'idle') {
187
+ summary.textContent = 'Loading debates…';
188
+ list.innerHTML = '<div class="debates-workbench-empty"><strong>Gathering your debates</strong>Planning sessions and completed debates will appear here.</div>';
189
+ } else if (status === 'error') {
190
+ summary.textContent = 'Debates could not be loaded.';
191
+ var failure = document.createElement('div');
192
+ failure.className = 'debates-workbench-empty';
193
+ var failureTitle = document.createElement('strong');
194
+ failureTitle.textContent = 'Could not load debates';
195
+ var failureText = document.createElement('span');
196
+ failureText.textContent = store.get('debatesWorkbenchError') || 'Try again when the connection is available.';
197
+ failure.appendChild(failureTitle);
198
+ failure.appendChild(failureText);
199
+ list.appendChild(failure);
200
+ } else if (!debates.length) {
201
+ summary.textContent = filter === 'all' ? 'No debates yet' : 'No ' + filter + ' debates';
202
+ list.innerHTML = '<div class="debates-workbench-empty"><strong>Nothing here yet</strong>Use New debate to plan one with Clay in the main chat.</div>';
203
+ } else {
204
+ summary.textContent = debates.length + (debates.length === 1 ? ' debate' : ' debates');
205
+ for (var i = 0; i < debates.length; i++) list.appendChild(debateRow(debates[i]));
206
+ }
207
+ list.scrollTop = Number(store.get('debatesWorkbenchScrollTop')) || 0;
208
+ refreshIcons();
209
+ }
210
+
211
+ function handleKeydown(event) {
212
+ if (event.key === 'Escape') {
213
+ event.preventDefault();
214
+ closeDebatesWorkbench(true);
215
+ return;
216
+ }
217
+ if (event.key !== 'Tab' || !narrowLayout()) return;
218
+ var controls = panel.querySelectorAll('button:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])');
219
+ if (!controls.length) return;
220
+ var first = controls[0];
221
+ var last = controls[controls.length - 1];
222
+ if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
223
+ else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
224
+ }
225
+
226
+ function applyWindowState(wide, fullscreen) {
227
+ store.set({ debatesWorkbenchWide: !!wide, debatesWorkbenchFullscreen: !!fullscreen });
228
+ panel.classList.toggle('debates-workbench-wide', !!wide && !fullscreen);
229
+ panel.classList.toggle('panel-fullscreen', !!fullscreen);
230
+ panel.querySelector('[data-debates-action="wide"]').setAttribute('aria-pressed', wide ? 'true' : 'false');
231
+ panel.querySelector('[data-debates-action="fullscreen"]').setAttribute('aria-pressed', fullscreen ? 'true' : 'false');
232
+ }
233
+
234
+ function handleClick(event) {
235
+ var filter = event.target.closest('[data-debates-filter]');
236
+ if (filter) { store.set({ debatesWorkbenchFilter: filter.dataset.debatesFilter }); render(); return; }
237
+ var row = event.target.closest('.debates-workbench-row');
238
+ if (row) {
239
+ var localId = Number(row.dataset.sessionId);
240
+ if (!row.dataset.projectSlug || !Number.isSafeInteger(localId) || localId < 1) return;
241
+ store.set({ pendingDebateNavigation: { projectSlug: row.dataset.projectSlug, sessionId: localId } });
242
+ if (narrowLayout()) closeDebatesWorkbench(false);
243
+ switchProject(row.dataset.projectSlug);
244
+ flushPendingNavigation();
245
+ return;
246
+ }
247
+ var action = event.target.closest('[data-debates-action]');
248
+ if (!action) return;
249
+ if (action.dataset.debatesAction === 'close') closeDebatesWorkbench(true);
250
+ if (action.dataset.debatesAction === 'new') {
251
+ store.set({ debatesWorkbenchCreating: true }); render();
252
+ panel.querySelector('textarea').focus({ preventScroll: true });
253
+ }
254
+ if (action.dataset.debatesAction === 'cancel-new') { store.set({ debatesWorkbenchCreating: false }); render(); }
255
+ if (action.dataset.debatesAction === 'wide') applyWindowState(!store.get('debatesWorkbenchWide'), false);
256
+ if (action.dataset.debatesAction === 'fullscreen') applyWindowState(false, !store.get('debatesWorkbenchFullscreen'));
257
+ }
258
+
259
+ function submitNewDebate(event) {
260
+ event.preventDefault();
261
+ var input = panel.querySelector('textarea');
262
+ var topic = input.value.trim();
263
+ if (!topic) { input.focus(); return; }
264
+ var status = panel.querySelector('.debates-workbench-launch-status');
265
+ status.textContent = 'Opening Clay in the main chat…';
266
+ if (!startProjectDebatePlanning(topic)) {
267
+ status.textContent = store.get('projectDebatePlanningError') || 'Clay is unavailable. Refresh and try again.';
268
+ input.focus();
269
+ return;
270
+ }
271
+ input.value = '';
272
+ store.set({ debatesWorkbenchCreating: false });
273
+ closeDebatesWorkbench();
274
+ }
275
+
276
+ function flushPendingNavigation() {
277
+ var pending = store.get('pendingDebateNavigation');
278
+ var ws = getWs();
279
+ if (!pending || store.get('activeProjectSlug') !== pending.projectSlug || !ws || ws.readyState !== 1) return;
280
+ store.set({ pendingDebateNavigation: null });
281
+ ws.send(JSON.stringify({ type: 'switch_session', id: pending.sessionId }));
282
+ }
283
+
284
+ export function handleDebatesWorkbenchState(msg) {
285
+ if (!msg || msg.type !== 'home_debates_state') return false;
286
+ if (!isMateDebateSurface(store.snap())) return true;
287
+ if (msg.requestId && msg.requestId !== store.get('debatesWorkbenchRequestId')) return true;
288
+ store.set({ debatesWorkbenchStatus: msg.status === 'error' || msg.error ? 'error' : 'ready', debatesWorkbenchItems: Array.isArray(msg.debates) ? msg.debates : [], debatesWorkbenchError: msg.error || '' });
289
+ render();
290
+ return true;
291
+ }
292
+
293
+ export function openDebatesWorkbench(trigger) {
294
+ if (!isMateDebateSurface(store.snap())) return false;
295
+ if (!ensurePanel()) return;
296
+ opener = trigger && trigger.focus ? trigger : document.activeElement;
297
+ claimRightWorkbench('debates');
298
+ panel.classList.remove('hidden');
299
+ var narrow = narrowLayout();
300
+ panel.setAttribute('role', narrow ? 'dialog' : 'region');
301
+ panel.setAttribute('aria-modal', narrow ? 'true' : 'false');
302
+ setNarrowBackground(narrow);
303
+ applyWindowState(store.get('debatesWorkbenchWide'), store.get('debatesWorkbenchFullscreen'));
304
+ requestDebates();
305
+ requestAnimationFrame(function () { panel.querySelector('[data-debates-action="new"]').focus({ preventScroll: true }); });
306
+ }
307
+
308
+ export function closeDebatesWorkbench(restoreFocus) {
309
+ if (panel) {
310
+ var list = panel.querySelector('.debates-workbench-list');
311
+ if (list) store.set({ debatesWorkbenchScrollTop: list.scrollTop });
312
+ panel.classList.add('hidden');
313
+ }
314
+ setNarrowBackground(false);
315
+ releaseRightWorkbench('debates');
316
+ if (restoreFocus && opener && opener.isConnected) opener.focus({ preventScroll: true });
317
+ }
318
+
319
+ export function initDebatesWorkbench() {
320
+ registerRightWorkbench('debates', function () { closeDebatesWorkbench(false); });
321
+ var buttons = [ensureMateProjectDebatesButton(), document.getElementById('debates-btn'), document.getElementById('mate-debates-btn')];
322
+ for (var i = 0; i < buttons.length; i++) if (buttons[i]) buttons[i].addEventListener('click', function (event) {
323
+ if (panel && !panel.classList.contains('hidden')) closeDebatesWorkbench(true);
324
+ else openDebatesWorkbench(event.currentTarget);
325
+ });
326
+ store.subscribe(function (state, previous) {
327
+ if (state.activeProjectSlug !== previous.activeProjectSlug || state.connected !== previous.connected) flushPendingNavigation();
328
+ var wasMateWorkspace = isMateDebateSurface(previous);
329
+ var isMate = isMateDebateSurface(state);
330
+ if (wasMateWorkspace && !isMate) {
331
+ requestSequence += 1;
332
+ store.set({ debatesWorkbenchRequestId: null });
333
+ if (panel && !panel.classList.contains('hidden')) closeDebatesWorkbench(false);
334
+ } else if (state.currentSlug !== previous.currentSlug && panel && !panel.classList.contains('hidden')) {
335
+ if (isMate) requestDebates();
336
+ else closeDebatesWorkbench(false);
337
+ }
338
+ });
339
+ }