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
@@ -1,3 +1,5 @@
1
+ import { confirmSettingsAction } from './server-settings-state.js';
2
+ import { enhanceSettingsSelects } from './settings-select.js';
1
3
  // User-island Default AI picker. Canonical state and the unsaved draft are separate.
2
4
 
3
5
  import { store } from './store.js';
@@ -7,7 +9,9 @@ import { refreshIcons } from './icons.js';
7
9
 
8
10
  var sequence = 0;
9
11
  var opener = null;
12
+ var preferredOpener = null;
10
13
  var popover = null;
14
+ var inlineTarget = null;
11
15
 
12
16
  function nextRequestId() {
13
17
  sequence += 1;
@@ -49,6 +53,7 @@ function close() {
49
53
 
50
54
  function position() {
51
55
  if (!popover || !opener) return;
56
+ if (inlineTarget) return;
52
57
  var rect = opener.getBoundingClientRect();
53
58
  var width = Math.min(390, Math.max(0, window.innerWidth - 24));
54
59
  popover.style.width = width + "px";
@@ -101,6 +106,41 @@ function addFieldLabel(text, control) {
101
106
  popover.appendChild(label);
102
107
  }
103
108
 
109
+ function inlineCurrentSelection(state, draft) {
110
+ return state.preference || state.selection || draft || {};
111
+ }
112
+
113
+ function renderInlineSummary() {
114
+ var state = store.get("defaultAiState") || {};
115
+ var draft = store.get("defaultAiDraft") || {};
116
+ var current = inlineCurrentSelection(state, draft);
117
+ var vendor = current.vendor ? (VENDOR_NAMES[current.vendor] || current.vendor) : "Loading default…";
118
+ var model = current.model || "Model unavailable";
119
+ var effort = current.effort || "Provider default";
120
+ popover.innerHTML = '';
121
+ var summary = document.createElement('button');
122
+ summary.type = 'button';
123
+ summary.className = 'default-ai-summary';
124
+ summary.id = 'account-ai-summary';
125
+ summary.setAttribute('aria-label', 'Default AI: ' + vendor + ', ' + model + ', ' + effort);
126
+ var label = document.createElement('span');
127
+ label.className = 'default-ai-summary-label';
128
+ label.textContent = 'Default AI';
129
+ var value = document.createElement('span');
130
+ value.className = 'default-ai-summary-value';
131
+ value.textContent = vendor + ' · ' + model + ' · ' + effort;
132
+ var icon = document.createElement('i');
133
+ icon.setAttribute('data-lucide', 'chevron-right');
134
+ icon.setAttribute('aria-hidden', 'true');
135
+ summary.appendChild(label);
136
+ summary.appendChild(value);
137
+ summary.appendChild(icon);
138
+ popover.appendChild(summary);
139
+ summary.addEventListener('click', function () { store.set({ defaultAiInlineEditing: true }); });
140
+ if(state.error)addStatus(state.error,true);
141
+ refreshIcons();
142
+ }
143
+
104
144
  function render() {
105
145
  if (!popover) return;
106
146
  var state = store.get("defaultAiState") || {};
@@ -109,7 +149,27 @@ function render() {
109
149
  var catalog = (state.catalogs || {})[draft.vendor] || null;
110
150
  var models = catalog && catalog.models || [];
111
151
 
112
- popover.innerHTML = '<div class="default-ai-title">Default AI</div><div class="default-ai-copy">Used for new Clay interviews and built-in AI assistance.</div>';
152
+ if (inlineTarget && store.get("defaultAiInlineEditing") !== true) {
153
+ renderInlineSummary();
154
+ position();
155
+ return;
156
+ }
157
+
158
+ if (inlineTarget) {
159
+ popover.innerHTML = '<div class="default-ai-edit-heading"><strong>Default AI</strong><button type="button" class="default-ai-cancel" aria-label="Cancel Default AI editing">Cancel</button></div>';
160
+ } else {
161
+ popover.innerHTML = '<div class="default-ai-title">Default AI</div><div class="default-ai-copy">Used for new Clay interviews and built-in AI assistance.</div>';
162
+ }
163
+ if (inlineTarget) {
164
+ var cancel = popover.querySelector(".default-ai-cancel");
165
+ cancel.addEventListener("click", function () {
166
+ function discard() {
167
+ var canonical = inlineCurrentSelection(store.get("defaultAiState") || {}, store.get("defaultAiDraft") || {});
168
+ store.set({ defaultAiDraft: Object.assign({}, canonical), defaultAiDraftDirty: false, defaultAiInlineEditing: false });
169
+ }
170
+ if(store.get("defaultAiDraftDirty"))confirmSettingsAction("Discard unsaved AI defaults?", "Discard", discard);else discard();
171
+ });
172
+ }
113
173
  if (state.loading && !vendors.length) {
114
174
  addStatus("Loading available AI providers…", false);
115
175
  position();
@@ -170,8 +230,8 @@ function render() {
170
230
  var save = document.createElement("button");
171
231
  save.type = "button";
172
232
  save.className = "default-ai-save";
173
- save.textContent = state.saving ? "Saving…" : "Save default";
174
- save.disabled = state.saving === true || vendors.indexOf(vendor.value) === -1 || !selectedModel || !selectedEffort;
233
+ save.textContent = state.saving ? "Applying…" : "Apply";
234
+ save.disabled = state.saving === true || store.get("defaultAiDraftDirty") !== true || vendors.indexOf(vendor.value) === -1 || !selectedModel || !selectedEffort;
175
235
  save.addEventListener("click", function () {
176
236
  var id = nextRequestId();
177
237
  var latest = store.get("defaultAiState") || {};
@@ -182,6 +242,7 @@ function render() {
182
242
  render();
183
243
  });
184
244
  popover.appendChild(save);
245
+ enhanceSettingsSelects(popover);
185
246
  if (state.error) addStatus(state.error, true);
186
247
  position();
187
248
  refreshIcons();
@@ -293,71 +354,25 @@ export function handleDefaultAiMessage(msg) {
293
354
  }
294
355
 
295
356
  export function initDefaultAi() {
296
- var actions = document.querySelector(".user-island-actions");
297
- if (!actions || document.getElementById("default-ai-btn")) return;
298
- var button = document.createElement("button");
299
- button.id = "default-ai-btn";
300
- button.className = "default-ai-button";
301
- button.title = "Default AI";
302
- button.setAttribute("aria-label", "Default AI");
303
- button.setAttribute("aria-haspopup", "dialog");
304
- button.setAttribute("aria-expanded", "false");
305
- button.innerHTML = '<span class="default-ai-avatar"></span>';
306
- var settings = document.getElementById("user-settings-btn");
307
- if (settings) actions.insertBefore(button, settings);
308
- else actions.appendChild(button);
309
- popover = document.createElement("section");
310
- popover.id = "default-ai-popover";
311
- popover.className = "default-ai-popover hidden";
312
- popover.setAttribute("role", "dialog");
313
- popover.setAttribute("aria-label", "Default AI settings");
314
- document.body.appendChild(popover);
315
- button.addEventListener("click", function () {
316
- if (!popover.classList.contains("hidden")) { close(); return; }
317
- opener = button;
318
- button.setAttribute("aria-expanded", "true");
319
- popover.classList.remove("hidden");
320
- requestDefaultAi();
321
- render();
322
- });
323
- document.addEventListener("click", function (event) {
324
- var path = typeof event.composedPath === "function" ? event.composedPath() : [];
325
- var clickedButton = path.indexOf(button) !== -1 || button.contains(event.target);
326
- var clickedPopover = path.indexOf(popover) !== -1 || popover.contains(event.target);
327
- if (!popover || popover.classList.contains("hidden") || clickedPopover || clickedButton) return;
328
- close();
329
- });
330
- document.addEventListener("keydown", function (event) {
331
- if (event.key !== "Escape" || !popover || popover.classList.contains("hidden")) return;
332
- event.preventDefault();
333
- close();
334
- });
335
- window.addEventListener("resize", position);
336
- window.addEventListener("scroll", position, true);
337
- updateBadge();
357
+ return;
338
358
  }
339
359
 
340
- export function openDefaultAi() {
341
- var button = document.getElementById("default-ai-btn");
342
- if (button && popover && popover.classList.contains("hidden")) button.click();
360
+ export function openDefaultAi(openerElement) {
361
+ document.dispatchEvent(new CustomEvent('clay:open-account-default-ai', { detail: { opener: openerElement || null } }));
343
362
  }
344
363
 
345
- function updateBadge() {
346
- var avatar = document.querySelector("#default-ai-btn .default-ai-avatar");
347
- var selection = (store.get("defaultAiState") || {}).selection;
348
- if (!avatar) return;
349
- var source = selection && VENDOR_AVATARS[selection.vendor];
350
- var image = avatar.querySelector("img");
351
- if (!source) {
352
- if (image) image.remove();
353
- return;
354
- }
355
- if (!image) {
356
- image = document.createElement("img");
357
- image.alt = "";
358
- avatar.appendChild(image);
359
- }
360
- if (image.getAttribute("src") !== source) image.setAttribute("src", source);
364
+ export function mountDefaultAi(target) {
365
+ inlineTarget = target;
366
+ popover = target;
367
+ opener = target;
368
+ render();
369
+ }
370
+
371
+ export function unmountDefaultAi(target) {
372
+ if (inlineTarget !== target) return;
373
+ inlineTarget = null;
374
+ opener = null;
375
+ popover = null;
361
376
  }
362
377
 
363
378
  store.subscribe(function (state, previous) {
@@ -371,7 +386,15 @@ store.subscribe(function (state, previous) {
371
386
  if (accountId && state.connected) requestDefaultAi();
372
387
  return;
373
388
  }
389
+ if (state.defaultAiInlineEditing !== previous.defaultAiInlineEditing) {
390
+ if (popover) render();
391
+ return;
392
+ }
374
393
  if (state.defaultAiState === previous.defaultAiState) return;
375
- updateBadge();
394
+ if (inlineTarget && previous.defaultAiState && previous.defaultAiState.saving === true && state.defaultAiState && state.defaultAiState.saving === false && state.defaultAiDraftDirty !== true && state.defaultAiInlineEditing === true) {
395
+ store.set({ defaultAiInlineEditing: false });
396
+ render();
397
+ return;
398
+ }
376
399
  if (popover && !popover.classList.contains("hidden")) render();
377
400
  });
@@ -7,23 +7,14 @@
7
7
  // PUT /api/user/terminal-font.
8
8
 
9
9
  import {
10
- applyTerminalFont,
10
+ saveTerminalFont,
11
+ TERMINAL_FONT_OPTIONS,
11
12
  getTerminalFontFamily,
12
13
  getTerminalFontSize,
13
14
  onTerminalFontChange,
14
15
  } from './terminal-prefs.js';
15
16
 
16
- var FONT_OPTIONS = [
17
- { label: "SF Mono", family: "'SF Mono', Menlo, Monaco, 'Courier New', monospace" },
18
- { label: "JetBrains Mono", family: "'JetBrains Mono', 'SF Mono', Menlo, monospace" },
19
- { label: "Fira Code", family: "'Fira Code', 'SF Mono', Menlo, monospace" },
20
- { label: "Cascadia Code", family: "'Cascadia Code', 'SF Mono', Menlo, monospace" },
21
- { label: "IBM Plex Mono", family: "'IBM Plex Mono', 'SF Mono', Menlo, monospace" },
22
- { label: "Source Code Pro", family: "'Source Code Pro', 'SF Mono', Menlo, monospace" },
23
- { label: "Roboto Mono", family: "'Roboto Mono', 'SF Mono', Menlo, monospace" },
24
- { label: "D2 Coding", family: "'D2 coding', 'D2Coding', 'SF Mono', Menlo, monospace" },
25
- { label: "System mono", family: "ui-monospace, monospace" },
26
- ];
17
+ var FONT_OPTIONS = TERMINAL_FONT_OPTIONS;
27
18
 
28
19
  var MIN_SIZE = 9;
29
20
  var MAX_SIZE = 32;
@@ -48,11 +39,12 @@ function escapeHtml(s) {
48
39
  }
49
40
 
50
41
  function persistTermFont(family, size) {
51
- fetch('/api/user/terminal-font', {
52
- method: 'PUT',
53
- headers: { 'Content-Type': 'application/json' },
54
- body: JSON.stringify({ family: family, size: size }),
55
- }).catch(function () {});
42
+ var status = popoverEl.querySelector('[role=status]');
43
+ status.textContent = 'Saving…';
44
+ popoverEl.querySelectorAll('button').forEach(function (button) {button.disabled = true;});
45
+ saveTerminalFont(family, size).then(function () {status.textContent = 'Saved.';})
46
+ .catch(function (error) {status.textContent = error.message;})
47
+ .finally(function () {popoverEl.querySelectorAll('button').forEach(function (button) {button.disabled = false;});syncSize();});
56
48
  }
57
49
 
58
50
  function syncSize() {
@@ -96,7 +88,6 @@ function buildPopover() {
96
88
  (function (family) {
97
89
  item.addEventListener('click', function (e) {
98
90
  e.stopPropagation();
99
- applyTerminalFont(family, undefined);
100
91
  persistTermFont(family, undefined);
101
92
  });
102
93
  })(opt.family);
@@ -127,16 +118,15 @@ function buildPopover() {
127
118
  sizeDecEl.addEventListener('click', function (e) {
128
119
  e.stopPropagation();
129
120
  var next = Math.max(MIN_SIZE, getTerminalFontSize() - 1);
130
- applyTerminalFont(undefined, next);
131
121
  persistTermFont(undefined, next);
132
122
  });
133
123
  sizeIncEl.addEventListener('click', function (e) {
134
124
  e.stopPropagation();
135
125
  var next = Math.min(MAX_SIZE, getTerminalFontSize() + 1);
136
- applyTerminalFont(undefined, next);
137
126
  persistTermFont(undefined, next);
138
127
  });
139
128
 
129
+ var status = document.createElement('p');status.setAttribute('role','status');status.className='settings-status';popoverEl.appendChild(status);
140
130
  document.body.appendChild(popoverEl);
141
131
  return popoverEl;
142
132
  }
@@ -1,295 +1,24 @@
1
1
  import { store } from './store.js';
2
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");
3
+ import { createDebateControls } from './debate-controls.js';
4
+ export { homeDebateControlState } from './debate-controls.js';
5
+
6
+ var controls = createDebateControls({
7
+ slotId: "home-debate-controls-slot",
8
+ composerId: "home-mate-chat-composer",
9
+ modelId: "home-mate-chat-session-model",
10
+ send: function (action, data, requestId) {
11
+ var ws = getWs();
12
+ if (!ws || ws.readyState !== 1) return false;
13
+ ws.send(JSON.stringify({
14
+ type: "home_debate_control", action: action,
15
+ mateId: store.get('homeChatMateId'), sessionId: store.get('homeChatSessionId'),
16
+ requestId: requestId, text: data.text || "", response: data.response || null
17
+ }));
18
+ return true;
271
19
  }
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
- }
20
+ });
278
21
 
279
22
  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;
23
+ return controls.render(messages, requestId, onStartNewDebate);
295
24
  }