clay-server 3.8.1 → 4.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/background-task-timing.js +105 -0
  3. package/lib/builtin-mates.js +6 -6
  4. package/lib/claude-hook-installer.js +15 -0
  5. package/lib/clay-history-mcp-server.js +62 -297
  6. package/lib/daemon.js +23 -1
  7. package/lib/debate-model-selection.js +119 -0
  8. package/lib/home-debate-tool-policy.js +164 -0
  9. package/lib/home-response-notifications.js +26 -0
  10. package/lib/knowledge-import.js +436 -0
  11. package/lib/knowledge-record-store.js +188 -0
  12. package/lib/knowledge-search.js +224 -0
  13. package/lib/mate-creation-mcp-server.js +55 -0
  14. package/lib/mate-knowledge-mcp-server.js +151 -0
  15. package/lib/mate-knowledge-migration.js +283 -0
  16. package/lib/mate-knowledge-service.js +488 -0
  17. package/lib/mate-knowledge-sync.js +482 -0
  18. package/lib/mate-ready-creation.js +95 -0
  19. package/lib/mates.js +2 -0
  20. package/lib/notes.js +2 -1
  21. package/lib/project-ask-user.js +64 -0
  22. package/lib/project-capsule-catalog.js +101 -0
  23. package/lib/project-connection.js +17 -0
  24. package/lib/project-debate-proposal.js +133 -17
  25. package/lib/project-debate.js +214 -110
  26. package/lib/project-delegated-follow-up.js +84 -0
  27. package/lib/project-delegated-session.js +53 -0
  28. package/lib/project-filesystem.js +7 -9
  29. package/lib/project-knowledge.js +23 -0
  30. package/lib/project-logs-comments.js +137 -0
  31. package/lib/project-logs-mcp-server.js +290 -0
  32. package/lib/project-logs-query.js +167 -0
  33. package/lib/project-logs-root.js +67 -0
  34. package/lib/project-logs-schema.js +259 -0
  35. package/lib/project-logs-service.js +357 -0
  36. package/lib/project-logs-snapshot.js +202 -0
  37. package/lib/project-logs-store.js +498 -0
  38. package/lib/project-logs-versioning.js +128 -0
  39. package/lib/project-logs.js +347 -0
  40. package/lib/project-mate-creation-proposal.js +120 -0
  41. package/lib/project-mate-interaction.js +14 -0
  42. package/lib/project-mate-knowledge.js +110 -0
  43. package/lib/project-memory.js +36 -7
  44. package/lib/project-models.js +157 -32
  45. package/lib/project-session-notes.js +2 -2
  46. package/lib/project-session-pair.js +8 -8
  47. package/lib/project-sessions.js +66 -51
  48. package/lib/project-vendor-login.js +414 -0
  49. package/lib/project-worker-proposal.js +25 -25
  50. package/lib/project-workspace-query.js +144 -0
  51. package/lib/project.js +433 -102
  52. package/lib/public/app.js +109 -48
  53. package/lib/public/css/avatar-imprints.css +0 -1
  54. package/lib/public/css/base.css +4 -0
  55. package/lib/public/css/capsule-ui.css +389 -0
  56. package/lib/public/css/command-palette.css +306 -6
  57. package/lib/public/css/home-capsule-library.css +243 -0
  58. package/lib/public/css/home-capsule-source.css +192 -0
  59. package/lib/public/css/home-debate-live.css +146 -0
  60. package/lib/public/css/home-debate-planning.css +125 -0
  61. package/lib/public/css/home-debates-archive.css +281 -0
  62. package/lib/public/css/home-hub.css +896 -405
  63. package/lib/public/css/home-mate-creation.css +34 -0
  64. package/lib/public/css/home-mate-model-picker.css +131 -0
  65. package/lib/public/css/home-mate-settings.css +344 -0
  66. package/lib/public/css/home-session-actions.css +265 -0
  67. package/lib/public/css/home-sidebar.css +667 -0
  68. package/lib/public/css/icon-strip.css +1 -2
  69. package/lib/public/css/input.css +79 -25
  70. package/lib/public/css/mates.css +7 -34
  71. package/lib/public/css/pane.css +1 -1
  72. package/lib/public/css/playbook.css +0 -80
  73. package/lib/public/css/project-logs.css +234 -0
  74. package/lib/public/css/scheduler.css +1 -1
  75. package/lib/public/css/sticky-notes.css +53 -117
  76. package/lib/public/css/tui-attention.css +0 -44
  77. package/lib/public/css/workspace-assignment.css +45 -0
  78. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  79. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  80. package/lib/public/index.html +96 -74
  81. package/lib/public/modules/app-connection.js +100 -5
  82. package/lib/public/modules/app-dm.js +43 -160
  83. package/lib/public/modules/app-home-hub.js +328 -585
  84. package/lib/public/modules/app-message-cards.js +242 -0
  85. package/lib/public/modules/app-message-router.js +84 -0
  86. package/lib/public/modules/app-messages.js +124 -38
  87. package/lib/public/modules/app-notifications.js +8 -119
  88. package/lib/public/modules/app-panels.js +43 -2
  89. package/lib/public/modules/app-projects.js +9 -1
  90. package/lib/public/modules/app-rendering.js +21 -602
  91. package/lib/public/modules/avatar-imprint.js +11 -8
  92. package/lib/public/modules/background-tasks-ui.js +139 -16
  93. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  94. package/lib/public/modules/chat-render-runtime.js +198 -0
  95. package/lib/public/modules/command-palette.js +225 -436
  96. package/lib/public/modules/dm-render.js +82 -0
  97. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  98. package/lib/public/modules/home-capsule-library.js +146 -0
  99. package/lib/public/modules/home-capsule-source.js +383 -0
  100. package/lib/public/modules/home-chat-identity.js +35 -0
  101. package/lib/public/modules/home-chat-scroll.js +77 -0
  102. package/lib/public/modules/home-chat-stream-state.js +34 -0
  103. package/lib/public/modules/home-composer-focus.js +25 -0
  104. package/lib/public/modules/home-conversations-sheet.js +179 -0
  105. package/lib/public/modules/home-debate-activity.js +48 -0
  106. package/lib/public/modules/home-debate-controls.js +295 -0
  107. package/lib/public/modules/home-debate-launch.js +41 -0
  108. package/lib/public/modules/home-debate-live.js +284 -0
  109. package/lib/public/modules/home-debate-models.js +73 -0
  110. package/lib/public/modules/home-debate-planning.js +335 -0
  111. package/lib/public/modules/home-debates-archive.js +279 -0
  112. package/lib/public/modules/home-dock-resize.js +74 -0
  113. package/lib/public/modules/home-dock.js +446 -0
  114. package/lib/public/modules/home-mate-chat.js +496 -0
  115. package/lib/public/modules/home-mate-creation.js +109 -0
  116. package/lib/public/modules/home-mate-model-picker.js +281 -0
  117. package/lib/public/modules/home-mate-selection.js +38 -0
  118. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  119. package/lib/public/modules/home-mate-settings.js +383 -0
  120. package/lib/public/modules/home-session-actions.js +373 -0
  121. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  122. package/lib/public/modules/home-shell.js +9 -0
  123. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  124. package/lib/public/modules/home-sidebar.js +165 -0
  125. package/lib/public/modules/home-sub-surface.js +19 -0
  126. package/lib/public/modules/home-surface-boot.js +68 -0
  127. package/lib/public/modules/home-surface.js +141 -0
  128. package/lib/public/modules/home-tools.js +271 -0
  129. package/lib/public/modules/markdown.js +50 -2
  130. package/lib/public/modules/mate-management.js +86 -0
  131. package/lib/public/modules/mate-sidebar.js +0 -8
  132. package/lib/public/modules/mate-wizard.js +0 -7
  133. package/lib/public/modules/mention.js +17 -19
  134. package/lib/public/modules/pane-bridge.js +9 -0
  135. package/lib/public/modules/profile.js +4 -5
  136. package/lib/public/modules/project-logs-ambient.js +238 -0
  137. package/lib/public/modules/project-logs-render.js +359 -0
  138. package/lib/public/modules/project-logs.js +328 -0
  139. package/lib/public/modules/project-settings.js +34 -28
  140. package/lib/public/modules/project-switcher.js +11 -5
  141. package/lib/public/modules/project-workspace-assignment.js +23 -0
  142. package/lib/public/modules/scheduler.js +17 -3
  143. package/lib/public/modules/search-clay-chat.js +402 -0
  144. package/lib/public/modules/server-settings.js +14 -12
  145. package/lib/public/modules/session-actions.js +1 -1
  146. package/lib/public/modules/sidebar-mates.js +56 -552
  147. package/lib/public/modules/sidebar-mobile.js +12 -73
  148. package/lib/public/modules/sidebar-presence.js +37 -0
  149. package/lib/public/modules/sidebar-sessions.js +25 -1
  150. package/lib/public/modules/split-pair-ui.js +13 -13
  151. package/lib/public/modules/split-view.js +7 -0
  152. package/lib/public/modules/sticky-notes.js +106 -53
  153. package/lib/public/modules/tool-input-composition.js +48 -0
  154. package/lib/public/modules/tool-llm-status.js +167 -0
  155. package/lib/public/modules/tool-palette.js +76 -4
  156. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  157. package/lib/public/modules/tool-renderer-chart.js +158 -0
  158. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  159. package/lib/public/modules/tool-renderer.js +464 -0
  160. package/lib/public/modules/tool-runtime.js +309 -0
  161. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  162. package/lib/public/modules/tool-ui-tree.js +21 -0
  163. package/lib/public/modules/tui-attention.js +10 -0
  164. package/lib/public/modules/user-settings.js +16 -23
  165. package/lib/public/modules/vendor-login.js +287 -0
  166. package/lib/public/modules/worker-proposal.js +2 -2
  167. package/lib/public/modules/workspace-assignment-card.js +149 -0
  168. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  169. package/lib/public/style.css +14 -2
  170. package/lib/runtime-env.js +65 -0
  171. package/lib/sdk-bridge.js +120 -32
  172. package/lib/sdk-message-processor.js +34 -13
  173. package/lib/server-home-capsule-creation.js +36 -0
  174. package/lib/server-home-chat-events.js +287 -0
  175. package/lib/server-home-chat.js +518 -0
  176. package/lib/server-home-clay-entry.js +65 -0
  177. package/lib/server-home-clay-session-links.js +26 -0
  178. package/lib/server-home-debate-planning.js +214 -0
  179. package/lib/server-home-debates.js +137 -0
  180. package/lib/server-home-mate-creation.js +261 -0
  181. package/lib/server-home-models.js +241 -0
  182. package/lib/server-home-preferences.js +61 -0
  183. package/lib/server-mates.js +37 -0
  184. package/lib/server-tools.js +377 -0
  185. package/lib/server.js +166 -59
  186. package/lib/session-pair-mcp-server.js +6 -6
  187. package/lib/sessions.js +22 -4
  188. package/lib/tool-capsule-source.js +142 -0
  189. package/lib/tool-control-mcp-server.js +126 -0
  190. package/lib/tool-llm.js +48 -0
  191. package/lib/tool-storage.js +77 -0
  192. package/lib/tool-ui-spec-advanced.js +82 -0
  193. package/lib/tool-ui-spec.js +261 -0
  194. package/lib/tools-registry.js +286 -0
  195. package/lib/users-home-dock-preferences.js +77 -0
  196. package/lib/users-home-surface-preferences.js +99 -0
  197. package/lib/users-preferences.js +4 -7
  198. package/lib/users.js +12 -0
  199. package/lib/whats-new-content.js +3 -3
  200. package/lib/workspace-assignment-service.js +420 -0
  201. package/lib/workspace-query-mcp-server.js +154 -0
  202. package/lib/workspace-query-service.js +492 -0
  203. package/lib/ws-schema.js +107 -12
  204. package/lib/yoke/adapters/acp.js +2 -1
  205. package/lib/yoke/adapters/antigravity.js +2 -1
  206. package/lib/yoke/adapters/claude-worker.js +11 -4
  207. package/lib/yoke/adapters/claude.js +141 -46
  208. package/lib/yoke/adapters/codex.js +79 -11
  209. package/lib/yoke/adapters/kiro.js +2 -1
  210. package/lib/yoke/claude-user-input.js +38 -0
  211. package/lib/yoke/codex-app-server.js +20 -2
  212. package/lib/yoke/codex-background-tasks.js +8 -2
  213. package/lib/yoke/codex-user-input.js +64 -0
  214. package/lib/yoke/complete-once.js +164 -0
  215. package/lib/yoke/index.js +5 -1
  216. package/lib/yoke/interface.js +19 -0
  217. package/lib/yoke/mcp-bridge-server.js +2 -6
  218. package/lib/yoke/user-input.js +254 -0
  219. package/package.json +4 -3
  220. package/lib/public/css/home-chat.css +0 -380
  221. package/lib/public/modules/home-chat.js +0 -438
  222. package/lib/server-clay-home.js +0 -245
@@ -0,0 +1,77 @@
1
+ // User-intent-aware scroll following for the rebuilt Home transcript DOM.
2
+
3
+ var messagesEl = null;
4
+ var activityButton = null;
5
+ var bound = false;
6
+ var adjusting = false;
7
+ var userScrolledUp = false;
8
+ var activityVersion = 0;
9
+ var seenActivityVersion = 0;
10
+ var followNext = false;
11
+ var BOTTOM_THRESHOLD = 150;
12
+
13
+ function resolveDom() {
14
+ if (!messagesEl) messagesEl = document.getElementById("home-mate-chat-messages");
15
+ if (!activityButton) activityButton = document.getElementById("home-chat-new-activity");
16
+ if (bound || !messagesEl || !activityButton) return;
17
+ bound = true;
18
+ messagesEl.addEventListener("scroll", function () {
19
+ if (adjusting) return;
20
+ var distance = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
21
+ userScrolledUp = distance > BOTTOM_THRESHOLD;
22
+ if (!userScrolledUp) {
23
+ seenActivityVersion = activityVersion;
24
+ activityButton.hidden = true;
25
+ }
26
+ }, { passive: true });
27
+ activityButton.addEventListener("click", function () {
28
+ userScrolledUp = false;
29
+ followNext = true;
30
+ activityButton.hidden = true;
31
+ adjusting = true;
32
+ messagesEl.scrollTop = messagesEl.scrollHeight;
33
+ requestAnimationFrame(function () { adjusting = false; });
34
+ });
35
+ }
36
+
37
+ export function markHomeChatActivity() {
38
+ activityVersion += 1;
39
+ }
40
+
41
+ export function followHomeChatNextRender() {
42
+ followNext = true;
43
+ userScrolledUp = false;
44
+ }
45
+
46
+ export function captureHomeChatScroll(force) {
47
+ resolveDom();
48
+ if (!messagesEl) return null;
49
+ var distance = messagesEl.scrollHeight - messagesEl.scrollTop - messagesEl.clientHeight;
50
+ var nearBottom = !isFinite(distance) || distance <= BOTTOM_THRESHOLD;
51
+ return {
52
+ follow: force === true || followNext || (!userScrolledUp && nearBottom),
53
+ scrollTop: messagesEl.scrollTop || 0,
54
+ hasActivity: activityVersion > seenActivityVersion,
55
+ };
56
+ }
57
+
58
+ export function restoreHomeChatScroll(snapshot) {
59
+ resolveDom();
60
+ if (!messagesEl || !snapshot) return;
61
+ followNext = false;
62
+ adjusting = true;
63
+ if (snapshot.follow) {
64
+ userScrolledUp = false;
65
+ seenActivityVersion = activityVersion;
66
+ if (activityButton) activityButton.hidden = true;
67
+ requestAnimationFrame(function () {
68
+ messagesEl.scrollTop = messagesEl.scrollHeight;
69
+ adjusting = false;
70
+ });
71
+ return;
72
+ }
73
+ messagesEl.scrollTop = snapshot.scrollTop;
74
+ userScrolledUp = true;
75
+ if (activityButton && snapshot.hasActivity) activityButton.hidden = false;
76
+ requestAnimationFrame(function () { adjusting = false; });
77
+ }
@@ -0,0 +1,34 @@
1
+ // Pure correlation and transcript helpers for the Home conversation stream.
2
+
3
+ export function isOwnedHomeSessionMessage(active, msg) {
4
+ if (!msg || msg.mateId !== active.mateId) return false;
5
+ if (msg.requestId && msg.requestId !== active.requestId) return false;
6
+ if (active.sessionId && msg.sessionId && msg.sessionId !== active.sessionId) return false;
7
+ return true;
8
+ }
9
+
10
+ export function resolveHomeSessionIdentity(active, msg) {
11
+ if (!msg || msg.mateId !== active.mateId) return null;
12
+ if (msg.requestId && msg.requestId !== active.requestId) return null;
13
+ if (!msg.previousSessionId || !msg.sessionId) return null;
14
+ if (active.sessionId !== msg.previousSessionId) return null;
15
+ return msg.sessionId;
16
+ }
17
+
18
+ export function appendHomeStreamText(current, incoming) {
19
+ return current + (typeof incoming === "string" ? incoming : "");
20
+ }
21
+
22
+ export function finalizeHomeAssistant(messages, streamingText, finalText, timestamp) {
23
+ var text = typeof finalText === "string" && finalText ? finalText : streamingText;
24
+ if (!text) return messages;
25
+ var last = messages.length ? messages[messages.length - 1] : null;
26
+ if (last && last.role === "assistant" && last.text === text) return messages;
27
+ var next = messages.slice();
28
+ if (last && last.role === "assistant" && text.indexOf(last.text) === 0) {
29
+ next[next.length - 1] = { role: "assistant", text: text, ts: timestamp };
30
+ return next;
31
+ }
32
+ next.push({ role: "assistant", text: text, ts: timestamp });
33
+ return next;
34
+ }
@@ -0,0 +1,25 @@
1
+ // Home composer submission bindings and one-shot focus restoration.
2
+
3
+ export function focusHomeComposerAfterSubmit(input) {
4
+ if (!input || input.disabled || input.isConnected === false) return false;
5
+ var home = typeof input.closest === "function" ? input.closest("#home-hub") : null;
6
+ if (!home || (home.classList && home.classList.contains("hidden"))) return false;
7
+ var ownerDocument = input.ownerDocument || document;
8
+ if (ownerDocument.hidden) return false;
9
+ if (ownerDocument.activeElement === input) return true;
10
+ input.focus({ preventScroll: true });
11
+ return true;
12
+ }
13
+
14
+ export function bindHomeComposerSubmission(input, sendButton, submit) {
15
+ function submitAndRestoreFocus() {
16
+ if (submit() !== true) return false;
17
+ return focusHomeComposerAfterSubmit(input);
18
+ }
19
+ input.addEventListener("keydown", function (event) {
20
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
21
+ event.preventDefault();
22
+ submitAndRestoreFocus();
23
+ });
24
+ sendButton.addEventListener("click", submitAndRestoreFocus);
25
+ }
@@ -0,0 +1,179 @@
1
+ // Searchable projection of every user-owned Mate conversation loaded by Home.
2
+
3
+ import { store } from './store.js';
4
+ import { iconHtml, refreshIcons } from './icons.js';
5
+ import { getHomeSessionConversations, createHomeSessionActionsTrigger, disposeHomeSessionActionsMenu } from './home-session-actions.js';
6
+
7
+ var overlay = null;
8
+ var searchInput = null;
9
+ var listEl = null;
10
+ var sheetOpener = null;
11
+ var selectConversation = null;
12
+
13
+ function closeSheet(restoreFocus) {
14
+ if (!overlay) return;
15
+ var opener = sheetOpener;
16
+ disposeHomeSessionActionsMenu();
17
+ overlay.remove();
18
+ overlay = null;
19
+ searchInput = null;
20
+ listEl = null;
21
+ sheetOpener = null;
22
+ selectConversation = null;
23
+ document.removeEventListener("keydown", handleKeydown, true);
24
+ if (restoreFocus !== false && opener && document.contains(opener) && typeof opener.focus === "function") opener.focus();
25
+ }
26
+
27
+ function focusableElements() {
28
+ if (!overlay) return [];
29
+ return Array.prototype.slice.call(overlay.querySelectorAll('button:not([disabled]):not([tabindex="-1"]), input:not([disabled]), [href], [tabindex]:not([tabindex="-1"])'));
30
+ }
31
+
32
+ function handleKeydown(event) {
33
+ if (event.key === "Escape") {
34
+ event.preventDefault();
35
+ event.stopPropagation();
36
+ closeSheet();
37
+ return;
38
+ }
39
+ if (event.key !== "Tab") return;
40
+ var focusable = focusableElements();
41
+ if (!focusable.length) {
42
+ event.preventDefault();
43
+ return;
44
+ }
45
+ var first = focusable[0];
46
+ var last = focusable[focusable.length - 1];
47
+ var active = document.activeElement;
48
+ if (event.shiftKey && (active === first || !overlay.contains(active))) {
49
+ event.preventDefault();
50
+ last.focus();
51
+ } else if (!event.shiftKey && (active === last || !overlay.contains(active))) {
52
+ event.preventDefault();
53
+ first.focus();
54
+ }
55
+ }
56
+
57
+ function renderRows() {
58
+ if (!listEl) return;
59
+ var query = searchInput ? searchInput.value.trim().toLowerCase() : "";
60
+ var all = getHomeSessionConversations().filter(function (conversation) {
61
+ if (!query) return true;
62
+ return conversation.title.toLowerCase().indexOf(query) !== -1 || conversation.mateName.toLowerCase().indexOf(query) !== -1;
63
+ });
64
+ disposeHomeSessionActionsMenu();
65
+ listEl.innerHTML = "";
66
+ if (!all.length) {
67
+ var empty = document.createElement("div");
68
+ empty.className = "home-conversations-sheet-empty";
69
+ empty.textContent = query ? "No chats match your search." : "No chats yet.";
70
+ listEl.appendChild(empty);
71
+ return;
72
+ }
73
+ for (var i = 0; i < all.length; i++) {
74
+ (function (conversation) {
75
+ var item = document.createElement("div");
76
+ item.className = "home-conversations-sheet-item";
77
+ var row = document.createElement("button");
78
+ row.type = "button";
79
+ row.className = "home-conversations-sheet-row";
80
+ var copy = document.createElement("span");
81
+ copy.className = "home-conversations-sheet-copy";
82
+ var title = document.createElement("span");
83
+ title.className = "home-conversations-sheet-row-title";
84
+ title.textContent = conversation.title;
85
+ copy.appendChild(title);
86
+ var mate = document.createElement("span");
87
+ mate.className = "home-conversations-sheet-row-mate";
88
+ mate.textContent = conversation.mateName;
89
+ copy.appendChild(mate);
90
+ row.appendChild(copy);
91
+ if (conversation.isProcessing) {
92
+ var activity = document.createElement("span");
93
+ activity.className = "home-sidebar-processing";
94
+ activity.setAttribute("aria-label", "Processing");
95
+ row.appendChild(activity);
96
+ }
97
+ row.addEventListener("click", function () {
98
+ var onSelect = selectConversation;
99
+ closeSheet();
100
+ if (onSelect) onSelect(conversation.mateId, conversation.sessionId);
101
+ });
102
+ var active = conversation.mateId === store.get('homeChatMateId') && conversation.sessionId === store.get('homeChatSessionId');
103
+ if (active) {
104
+ item.classList.add("is-active");
105
+ row.setAttribute("aria-current", "page");
106
+ }
107
+ var detailsReturn = sheetOpener;
108
+ var actions = createHomeSessionActionsTrigger(conversation, {
109
+ detailsOpener: detailsReturn,
110
+ beforeOpenDetails: function () { closeSheet(false); },
111
+ });
112
+ item.appendChild(row);
113
+ item.appendChild(actions);
114
+ listEl.appendChild(item);
115
+ })(all[i]);
116
+ }
117
+ }
118
+
119
+ export function openHomeConversationsSheet(onSelect, opener) {
120
+ closeSheet();
121
+ sheetOpener = opener || document.activeElement;
122
+ selectConversation = onSelect;
123
+ overlay = document.createElement("div");
124
+ overlay.className = "home-conversations-sheet";
125
+ overlay.setAttribute("role", "dialog");
126
+ overlay.setAttribute("aria-modal", "true");
127
+ overlay.setAttribute("aria-labelledby", "home-conversations-sheet-title");
128
+
129
+ var backdrop = document.createElement("button");
130
+ backdrop.type = "button";
131
+ backdrop.className = "home-conversations-sheet-backdrop";
132
+ backdrop.setAttribute("aria-label", "Close chats");
133
+ backdrop.tabIndex = -1;
134
+ backdrop.addEventListener("click", closeSheet);
135
+ overlay.appendChild(backdrop);
136
+
137
+ var panel = document.createElement("section");
138
+ panel.className = "home-conversations-sheet-panel";
139
+ var header = document.createElement("header");
140
+ header.className = "home-conversations-sheet-header";
141
+ var title = document.createElement("div");
142
+ title.id = "home-conversations-sheet-title";
143
+ title.className = "home-conversations-sheet-title";
144
+ title.textContent = "All chats";
145
+ header.appendChild(title);
146
+ var close = document.createElement("button");
147
+ close.type = "button";
148
+ close.className = "home-sidebar-icon-btn";
149
+ close.setAttribute("aria-label", "Close chats");
150
+ close.innerHTML = iconHtml("x");
151
+ close.addEventListener("click", closeSheet);
152
+ header.appendChild(close);
153
+ panel.appendChild(header);
154
+
155
+ var search = document.createElement("label");
156
+ search.className = "home-conversations-sheet-search";
157
+ search.innerHTML = iconHtml("search");
158
+ searchInput = document.createElement("input");
159
+ searchInput.type = "search";
160
+ searchInput.placeholder = "Search chats";
161
+ searchInput.setAttribute("aria-label", "Search chats");
162
+ searchInput.addEventListener("input", renderRows);
163
+ search.appendChild(searchInput);
164
+ panel.appendChild(search);
165
+
166
+ listEl = document.createElement("div");
167
+ listEl.className = "home-conversations-sheet-list";
168
+ panel.appendChild(listEl);
169
+ overlay.appendChild(panel);
170
+ document.body.appendChild(overlay);
171
+ document.addEventListener("keydown", handleKeydown, true);
172
+ renderRows();
173
+ refreshIcons();
174
+ searchInput.focus();
175
+ }
176
+
177
+ export function refreshHomeConversationsSheet() {
178
+ if (overlay) renderRows();
179
+ }
@@ -0,0 +1,48 @@
1
+ // Shared ephemeral activity row for Home debate planning.
2
+
3
+ var announced = {};
4
+
5
+ export function resetHomeDebateActivityAnnouncement(modifier) {
6
+ if (modifier) delete announced[modifier];
7
+ }
8
+
9
+ function textElement(tag, className, text) {
10
+ var element = document.createElement(tag);
11
+ element.className = className;
12
+ element.textContent = text;
13
+ return element;
14
+ }
15
+
16
+ export function createHomeDebateActivityRow(statusText, accessibleText, modifier) {
17
+ var announcementKey = modifier || "home-debate-activity";
18
+ var row = document.createElement("div");
19
+ row.className = "home-debate-activity " + announcementKey;
20
+ row.setAttribute("role", "status");
21
+ row.setAttribute("aria-live", announced[announcementKey] ? "off" : "polite");
22
+ row.setAttribute("aria-atomic", "true");
23
+ row.setAttribute("aria-label", accessibleText);
24
+ announced[announcementKey] = true;
25
+
26
+ var avatar = document.createElement("img");
27
+ avatar.className = "home-debate-activity-avatar";
28
+ avatar.src = "/clay-studio-symbol.png";
29
+ avatar.alt = "";
30
+ avatar.width = 28;
31
+ avatar.height = 28;
32
+ row.appendChild(avatar);
33
+
34
+ var content = document.createElement("div");
35
+ content.className = "home-debate-activity-content";
36
+ content.appendChild(textElement("span", "home-debate-activity-name", "Clay"));
37
+ var status = document.createElement("div");
38
+ status.className = "home-debate-activity-status";
39
+ status.appendChild(textElement("span", "home-debate-activity-label", statusText));
40
+ var dots = document.createElement("span");
41
+ dots.className = "home-debate-activity-dots";
42
+ dots.setAttribute("aria-hidden", "true");
43
+ for (var i = 0; i < 3; i++) dots.appendChild(document.createElement("i"));
44
+ status.appendChild(dots);
45
+ content.appendChild(status);
46
+ row.appendChild(content);
47
+ return row;
48
+ }
@@ -0,0 +1,295 @@
1
+ import { store } from './store.js';
2
+ import { getWs } from './ws-ref.js';
3
+
4
+ var slotEl = null;
5
+ var normalComposerEl = null;
6
+ var modelEl = null;
7
+ var controlEl = null;
8
+ var controlMode = null;
9
+ var controlStateKey = null;
10
+ var currentState = null;
11
+ var currentRequestId = null;
12
+ var startNewDebate = null;
13
+ var inputEl = null;
14
+ var primaryEl = null;
15
+ var secondaryEl = null;
16
+ var activityEl = null;
17
+ var handEl = null;
18
+ var stopEl = null;
19
+ var composing = false;
20
+
21
+ function textElement(tag, className, text) {
22
+ var element = document.createElement(tag);
23
+ element.className = className;
24
+ element.textContent = text || "";
25
+ return element;
26
+ }
27
+
28
+ function findHeader(messages) {
29
+ for (var i = (messages || []).length - 1; i >= 0; i--) if (messages[i] && messages[i].role === "debate_header") return messages[i];
30
+ return null;
31
+ }
32
+
33
+ function findActiveTurn(messages) {
34
+ for (var i = (messages || []).length - 1; i >= 0; i--) {
35
+ if (messages[i] && messages[i].role === "debate_turn" && messages[i].status === "active") return messages[i];
36
+ }
37
+ return null;
38
+ }
39
+
40
+ export function homeDebateControlState(messages) {
41
+ var header = findHeader(messages);
42
+ if (!header || ["live", "ended", "interrupted"].indexOf(header.phase) === -1) return null;
43
+ var mode = header.phase === "live" ? (header.interaction || "default") : "terminal";
44
+ return { mode: mode, header: header, turn: findActiveTurn(messages), phase: header.phase };
45
+ }
46
+
47
+ function ensureElements() {
48
+ var nextSlot = document.getElementById("home-debate-controls-slot");
49
+ if (slotEl === nextSlot) return !!slotEl;
50
+ slotEl = nextSlot;
51
+ normalComposerEl = document.getElementById("home-mate-chat-composer");
52
+ modelEl = document.getElementById("home-mate-chat-session-model");
53
+ controlEl = null;
54
+ controlMode = null;
55
+ controlStateKey = null;
56
+ return !!slotEl;
57
+ }
58
+
59
+ function clearControls() {
60
+ if (slotEl) slotEl.innerHTML = "";
61
+ controlEl = null;
62
+ controlMode = null;
63
+ controlStateKey = null;
64
+ currentState = null;
65
+ inputEl = null;
66
+ primaryEl = null;
67
+ secondaryEl = null;
68
+ activityEl = null;
69
+ handEl = null;
70
+ stopEl = null;
71
+ composing = false;
72
+ }
73
+
74
+ function setSpecialMode(active) {
75
+ if (normalComposerEl) normalComposerEl.hidden = active;
76
+ if (modelEl) modelEl.hidden = active;
77
+ if (slotEl) slotEl.hidden = !active;
78
+ }
79
+
80
+ function sendControl(action, data) {
81
+ var ws = getWs();
82
+ if (!ws || ws.readyState !== 1 || !currentState) return false;
83
+ ws.send(JSON.stringify({
84
+ type: "home_debate_control",
85
+ action: action,
86
+ mateId: store.get('homeChatMateId'),
87
+ sessionId: store.get('homeChatSessionId'),
88
+ requestId: currentRequestId,
89
+ text: data && data.text ? data.text : "",
90
+ response: data && data.response ? data.response : null,
91
+ }));
92
+ return true;
93
+ }
94
+
95
+ function button(label, className) {
96
+ var element = textElement("button", className, label);
97
+ element.type = "button";
98
+ return element;
99
+ }
100
+
101
+ function dots() {
102
+ var group = document.createElement("span");
103
+ group.className = "home-debate-control-dots";
104
+ group.setAttribute("aria-hidden", "true");
105
+ for (var i = 0; i < 3; i++) group.appendChild(document.createElement("i"));
106
+ return group;
107
+ }
108
+
109
+ function activityLabel(state) {
110
+ if (state.header.stopping) return state.turn ? "Stopping after " + (state.turn.mateName || "the current speaker") + " finishes" : "Stopping debate";
111
+ if (!state.turn) return state.header.handRaised ? "Your hand is raised" : "Debate is live";
112
+ var name = state.turn.mateName || "Mate";
113
+ var activity = state.turn.activity || (state.turn.text ? "speaking" : "preparing");
114
+ return name + " is " + activity.toLowerCase();
115
+ }
116
+
117
+ function lockSubmission(label) {
118
+ if (inputEl) inputEl.disabled = true;
119
+ if (primaryEl) { primaryEl.disabled = true; primaryEl.textContent = label; }
120
+ if (secondaryEl) secondaryEl.disabled = true;
121
+ }
122
+
123
+ function submitInput(action, response) {
124
+ var text = inputEl ? inputEl.value.trim() : "";
125
+ if (action === "user_floor" && !text) return false;
126
+ if (!sendControl(action, { text: text, response: response })) return false;
127
+ lockSubmission(action === "user_floor" ? "Sending…" : "Continuing…");
128
+ return true;
129
+ }
130
+
131
+ function bindInput(submit) {
132
+ inputEl.addEventListener("compositionstart", function () { composing = true; });
133
+ inputEl.addEventListener("compositionend", function () { composing = false; });
134
+ inputEl.addEventListener("input", function () {
135
+ if (controlMode === "user_floor" && primaryEl) primaryEl.disabled = !inputEl.value.trim();
136
+ });
137
+ inputEl.addEventListener("keydown", function (event) {
138
+ if (event.key !== "Enter" || event.shiftKey || event.isComposing || composing || event.keyCode === 229) return;
139
+ event.preventDefault();
140
+ submit();
141
+ });
142
+ }
143
+
144
+ function focusInputOnce() {
145
+ var target = inputEl;
146
+ if (!target) return;
147
+ var focus = function () {
148
+ if (target === inputEl && !target.disabled && target.isConnected !== false) target.focus({ preventScroll: true });
149
+ };
150
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(focus);
151
+ else focus();
152
+ }
153
+
154
+ function buildDefault(state) {
155
+ var context = document.createElement("div");
156
+ context.className = "home-debate-control-context";
157
+ context.appendChild(dots());
158
+ activityEl = textElement("span", "home-debate-control-activity", activityLabel(state));
159
+ activityEl.setAttribute("role", "status");
160
+ activityEl.setAttribute("aria-live", "polite");
161
+ context.appendChild(activityEl);
162
+ controlEl.appendChild(context);
163
+ var actions = document.createElement("div");
164
+ actions.className = "home-debate-control-actions";
165
+ handEl = button(state.header.handRaised ? "Hand raised" : "Raise hand", "home-debate-control-secondary");
166
+ handEl.setAttribute("aria-pressed", state.header.handRaised ? "true" : "false");
167
+ handEl.disabled = state.header.handRaised === true;
168
+ handEl.addEventListener("click", function () {
169
+ if (!sendControl("hand_raise")) return;
170
+ handEl.disabled = true;
171
+ handEl.textContent = "Raising hand…";
172
+ });
173
+ stopEl = button(state.header.stopping ? "Cancel stop" : "Stop debate", state.header.stopping ? "home-debate-control-secondary" : "home-debate-control-stop");
174
+ stopEl.addEventListener("click", function () {
175
+ var cancelling = currentState && currentState.header && currentState.header.stopping === true;
176
+ if (!sendControl(cancelling ? "cancel_stop" : "stop")) return;
177
+ stopEl.disabled = true;
178
+ stopEl.textContent = cancelling ? "Cancelling…" : "Stopping…";
179
+ });
180
+ actions.appendChild(handEl);
181
+ actions.appendChild(stopEl);
182
+ controlEl.appendChild(actions);
183
+ }
184
+
185
+ function buildInputMode(state) {
186
+ var isFloor = state.mode === "user_floor";
187
+ var heading = textElement("div", "home-debate-control-heading", isFloor ? "You have the floor" : "The moderator is ready to conclude");
188
+ heading.id = "home-debate-control-heading";
189
+ controlEl.appendChild(heading);
190
+ inputEl = document.createElement("textarea");
191
+ inputEl.rows = 2;
192
+ inputEl.placeholder = isFloor ? "Share your thoughts with the panel" : "Add an optional direction to continue";
193
+ inputEl.setAttribute("aria-labelledby", heading.id);
194
+ controlEl.appendChild(inputEl);
195
+ var actions = document.createElement("div");
196
+ actions.className = "home-debate-control-actions";
197
+ if (isFloor) {
198
+ primaryEl = button("Send", "home-debate-control-primary");
199
+ primaryEl.disabled = true;
200
+ primaryEl.addEventListener("click", function () { submitInput("user_floor"); });
201
+ secondaryEl = button("Pass", "home-debate-control-secondary");
202
+ secondaryEl.addEventListener("click", function () {
203
+ if (!sendControl("user_floor", { text: "(The user passed without speaking)" })) return;
204
+ lockSubmission("Sending…");
205
+ });
206
+ } else {
207
+ primaryEl = button("Continue", "home-debate-control-primary");
208
+ primaryEl.addEventListener("click", function () { submitInput("conclude", "continue"); });
209
+ secondaryEl = button("End debate", "home-debate-control-stop");
210
+ secondaryEl.addEventListener("click", function () {
211
+ if (!sendControl("conclude", { response: "end" })) return;
212
+ lockSubmission("Ending…");
213
+ });
214
+ }
215
+ actions.appendChild(primaryEl);
216
+ actions.appendChild(secondaryEl);
217
+ controlEl.appendChild(actions);
218
+ bindInput(function () { submitInput(isFloor ? "user_floor" : "conclude", isFloor ? null : "continue"); });
219
+ focusInputOnce();
220
+ }
221
+
222
+ function terminalLabel(state) {
223
+ if (state.phase === "interrupted") return "Debate interrupted when Clay restarted";
224
+ if (state.header.reason === "error") return "The debate stopped after an error";
225
+ if (state.header.reason === "stopped" || state.header.reason === "user_stopped") return "Debate stopped";
226
+ return "Debate ended";
227
+ }
228
+
229
+ function buildTerminal(state) {
230
+ var status = textElement("div", "home-debate-control-terminal-status", terminalLabel(state));
231
+ status.setAttribute("role", "status");
232
+ controlEl.appendChild(status);
233
+ var actions = document.createElement("div");
234
+ actions.className = "home-debate-control-actions";
235
+ primaryEl = button("Resume debate", "home-debate-control-primary");
236
+ primaryEl.addEventListener("click", function () {
237
+ if (!sendControl("resume")) return;
238
+ lockSubmission("Resuming…");
239
+ });
240
+ actions.appendChild(primaryEl);
241
+ if (typeof startNewDebate === "function") {
242
+ secondaryEl = button("New debate", "home-debate-control-secondary");
243
+ secondaryEl.addEventListener("click", function () { startNewDebate(); });
244
+ actions.appendChild(secondaryEl);
245
+ }
246
+ controlEl.appendChild(actions);
247
+ }
248
+
249
+ function buildControls(state) {
250
+ slotEl.innerHTML = "";
251
+ controlEl = document.createElement("section");
252
+ controlEl.className = "home-debate-control-surface home-debate-control-" + state.mode;
253
+ controlEl.setAttribute("role", state.mode === "default" ? "toolbar" : "region");
254
+ controlEl.setAttribute("aria-label", state.mode === "terminal" ? "Debate status" : "Live debate controls");
255
+ slotEl.appendChild(controlEl);
256
+ if (state.mode === "default") buildDefault(state);
257
+ else if (state.mode === "user_floor" || state.mode === "conclude") buildInputMode(state);
258
+ else buildTerminal(state);
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
+ export function renderHomeDebateControls(messages, requestId, onStartNewDebate) {
280
+ if (!ensureElements()) return false;
281
+ var state = homeDebateControlState(messages);
282
+ currentRequestId = requestId || null;
283
+ startNewDebate = onStartNewDebate || null;
284
+ if (!state) {
285
+ setSpecialMode(false);
286
+ clearControls();
287
+ return false;
288
+ }
289
+ currentState = state;
290
+ setSpecialMode(true);
291
+ var nextStateKey = state.mode + ":" + state.phase + ":" + (state.header.reason || "");
292
+ if (!controlEl || controlMode !== state.mode || controlStateKey !== nextStateKey) buildControls(state);
293
+ else updateControls(state);
294
+ return true;
295
+ }