clay-server 3.8.1 → 4.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/lib/ask-user-mcp-server.js +5 -119
  2. package/lib/builtin-mates.js +6 -6
  3. package/lib/claude-hook-installer.js +15 -0
  4. package/lib/clay-history-mcp-server.js +62 -297
  5. package/lib/daemon.js +1 -1
  6. package/lib/debate-model-selection.js +119 -0
  7. package/lib/home-debate-tool-policy.js +164 -0
  8. package/lib/home-response-notifications.js +26 -0
  9. package/lib/mate-creation-mcp-server.js +55 -0
  10. package/lib/mate-ready-creation.js +95 -0
  11. package/lib/mates.js +2 -0
  12. package/lib/notes.js +2 -1
  13. package/lib/project-ask-user.js +64 -0
  14. package/lib/project-capsule-catalog.js +101 -0
  15. package/lib/project-connection.js +4 -0
  16. package/lib/project-debate-proposal.js +133 -17
  17. package/lib/project-debate.js +214 -110
  18. package/lib/project-delegated-follow-up.js +84 -0
  19. package/lib/project-delegated-session.js +53 -0
  20. package/lib/project-filesystem.js +7 -9
  21. package/lib/project-knowledge.js +1 -0
  22. package/lib/project-mate-creation-proposal.js +120 -0
  23. package/lib/project-memory.js +14 -7
  24. package/lib/project-models.js +157 -32
  25. package/lib/project-session-notes.js +2 -2
  26. package/lib/project-session-pair.js +8 -8
  27. package/lib/project-sessions.js +66 -51
  28. package/lib/project-vendor-login.js +397 -0
  29. package/lib/project-worker-proposal.js +25 -25
  30. package/lib/project-workspace-query.js +144 -0
  31. package/lib/project.js +349 -102
  32. package/lib/public/app.js +85 -42
  33. package/lib/public/css/avatar-imprints.css +0 -1
  34. package/lib/public/css/base.css +4 -0
  35. package/lib/public/css/capsule-ui.css +389 -0
  36. package/lib/public/css/command-palette.css +306 -6
  37. package/lib/public/css/home-capsule-library.css +243 -0
  38. package/lib/public/css/home-capsule-source.css +192 -0
  39. package/lib/public/css/home-debate-live.css +146 -0
  40. package/lib/public/css/home-debate-planning.css +125 -0
  41. package/lib/public/css/home-debates-archive.css +281 -0
  42. package/lib/public/css/home-hub.css +896 -405
  43. package/lib/public/css/home-mate-creation.css +34 -0
  44. package/lib/public/css/home-mate-model-picker.css +131 -0
  45. package/lib/public/css/home-mate-settings.css +344 -0
  46. package/lib/public/css/home-session-actions.css +265 -0
  47. package/lib/public/css/home-sidebar.css +667 -0
  48. package/lib/public/css/icon-strip.css +1 -2
  49. package/lib/public/css/mates.css +7 -34
  50. package/lib/public/css/pane.css +1 -1
  51. package/lib/public/css/playbook.css +0 -80
  52. package/lib/public/css/sticky-notes.css +53 -117
  53. package/lib/public/css/tui-attention.css +0 -44
  54. package/lib/public/css/workspace-assignment.css +45 -0
  55. package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
  56. package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
  57. package/lib/public/index.html +93 -74
  58. package/lib/public/modules/app-connection.js +10 -1
  59. package/lib/public/modules/app-dm.js +43 -160
  60. package/lib/public/modules/app-home-hub.js +328 -585
  61. package/lib/public/modules/app-message-cards.js +242 -0
  62. package/lib/public/modules/app-message-router.js +84 -0
  63. package/lib/public/modules/app-messages.js +102 -37
  64. package/lib/public/modules/app-notifications.js +8 -119
  65. package/lib/public/modules/app-projects.js +9 -1
  66. package/lib/public/modules/app-rendering.js +21 -602
  67. package/lib/public/modules/avatar-imprint.js +11 -8
  68. package/lib/public/modules/chat-bubble-renderer.js +152 -0
  69. package/lib/public/modules/chat-render-runtime.js +198 -0
  70. package/lib/public/modules/command-palette.js +225 -436
  71. package/lib/public/modules/dm-render.js +82 -0
  72. package/lib/public/modules/home-capsule-creation-intent.js +31 -0
  73. package/lib/public/modules/home-capsule-library.js +146 -0
  74. package/lib/public/modules/home-capsule-source.js +383 -0
  75. package/lib/public/modules/home-chat-identity.js +35 -0
  76. package/lib/public/modules/home-chat-scroll.js +77 -0
  77. package/lib/public/modules/home-chat-stream-state.js +34 -0
  78. package/lib/public/modules/home-composer-focus.js +25 -0
  79. package/lib/public/modules/home-conversations-sheet.js +179 -0
  80. package/lib/public/modules/home-debate-activity.js +48 -0
  81. package/lib/public/modules/home-debate-controls.js +295 -0
  82. package/lib/public/modules/home-debate-launch.js +41 -0
  83. package/lib/public/modules/home-debate-live.js +284 -0
  84. package/lib/public/modules/home-debate-models.js +73 -0
  85. package/lib/public/modules/home-debate-planning.js +335 -0
  86. package/lib/public/modules/home-debates-archive.js +279 -0
  87. package/lib/public/modules/home-dock-resize.js +74 -0
  88. package/lib/public/modules/home-dock.js +446 -0
  89. package/lib/public/modules/home-mate-chat.js +496 -0
  90. package/lib/public/modules/home-mate-creation.js +109 -0
  91. package/lib/public/modules/home-mate-model-picker.js +281 -0
  92. package/lib/public/modules/home-mate-selection.js +38 -0
  93. package/lib/public/modules/home-mate-settings-menu.js +131 -0
  94. package/lib/public/modules/home-mate-settings.js +383 -0
  95. package/lib/public/modules/home-session-actions.js +373 -0
  96. package/lib/public/modules/home-session-model-confirmation.js +13 -0
  97. package/lib/public/modules/home-shell.js +9 -0
  98. package/lib/public/modules/home-sidebar-chat-list.js +63 -0
  99. package/lib/public/modules/home-sidebar.js +158 -0
  100. package/lib/public/modules/home-sub-surface.js +19 -0
  101. package/lib/public/modules/home-surface-boot.js +68 -0
  102. package/lib/public/modules/home-surface.js +141 -0
  103. package/lib/public/modules/home-tools.js +271 -0
  104. package/lib/public/modules/markdown.js +50 -2
  105. package/lib/public/modules/mate-management.js +86 -0
  106. package/lib/public/modules/mate-wizard.js +0 -7
  107. package/lib/public/modules/mention.js +17 -19
  108. package/lib/public/modules/pane-bridge.js +9 -0
  109. package/lib/public/modules/profile.js +4 -5
  110. package/lib/public/modules/project-settings.js +34 -28
  111. package/lib/public/modules/project-switcher.js +11 -5
  112. package/lib/public/modules/project-workspace-assignment.js +23 -0
  113. package/lib/public/modules/search-clay-chat.js +402 -0
  114. package/lib/public/modules/server-settings.js +14 -12
  115. package/lib/public/modules/session-actions.js +1 -1
  116. package/lib/public/modules/sidebar-mates.js +56 -552
  117. package/lib/public/modules/sidebar-mobile.js +7 -64
  118. package/lib/public/modules/sidebar-presence.js +37 -0
  119. package/lib/public/modules/split-pair-ui.js +13 -13
  120. package/lib/public/modules/split-view.js +7 -0
  121. package/lib/public/modules/sticky-notes.js +7 -6
  122. package/lib/public/modules/tool-input-composition.js +48 -0
  123. package/lib/public/modules/tool-llm-status.js +167 -0
  124. package/lib/public/modules/tool-renderer-advanced.js +285 -0
  125. package/lib/public/modules/tool-renderer-chart.js +158 -0
  126. package/lib/public/modules/tool-renderer-semantics.js +35 -0
  127. package/lib/public/modules/tool-renderer.js +464 -0
  128. package/lib/public/modules/tool-runtime.js +309 -0
  129. package/lib/public/modules/tool-ui-evaluator.js +97 -0
  130. package/lib/public/modules/tool-ui-tree.js +21 -0
  131. package/lib/public/modules/tui-attention.js +10 -0
  132. package/lib/public/modules/user-settings.js +16 -23
  133. package/lib/public/modules/vendor-login.js +287 -0
  134. package/lib/public/modules/worker-proposal.js +2 -2
  135. package/lib/public/modules/workspace-assignment-card.js +149 -0
  136. package/lib/public/modules/workspace-assignment-routing.js +4 -0
  137. package/lib/public/style.css +13 -2
  138. package/lib/runtime-env.js +65 -0
  139. package/lib/sdk-bridge.js +120 -32
  140. package/lib/sdk-message-processor.js +10 -6
  141. package/lib/server-home-capsule-creation.js +36 -0
  142. package/lib/server-home-chat-events.js +287 -0
  143. package/lib/server-home-chat.js +518 -0
  144. package/lib/server-home-clay-entry.js +65 -0
  145. package/lib/server-home-clay-session-links.js +26 -0
  146. package/lib/server-home-debate-planning.js +214 -0
  147. package/lib/server-home-debates.js +137 -0
  148. package/lib/server-home-mate-creation.js +261 -0
  149. package/lib/server-home-models.js +241 -0
  150. package/lib/server-home-preferences.js +61 -0
  151. package/lib/server-mates.js +37 -0
  152. package/lib/server-tools.js +377 -0
  153. package/lib/server.js +111 -59
  154. package/lib/session-pair-mcp-server.js +6 -6
  155. package/lib/sessions.js +18 -3
  156. package/lib/tool-capsule-source.js +142 -0
  157. package/lib/tool-control-mcp-server.js +126 -0
  158. package/lib/tool-llm.js +48 -0
  159. package/lib/tool-storage.js +77 -0
  160. package/lib/tool-ui-spec-advanced.js +82 -0
  161. package/lib/tool-ui-spec.js +261 -0
  162. package/lib/tools-registry.js +286 -0
  163. package/lib/users-home-dock-preferences.js +77 -0
  164. package/lib/users-home-surface-preferences.js +99 -0
  165. package/lib/users-preferences.js +4 -7
  166. package/lib/users.js +12 -0
  167. package/lib/whats-new-content.js +3 -3
  168. package/lib/workspace-assignment-service.js +420 -0
  169. package/lib/workspace-query-mcp-server.js +154 -0
  170. package/lib/workspace-query-service.js +492 -0
  171. package/lib/ws-schema.js +93 -12
  172. package/lib/yoke/adapters/acp.js +2 -1
  173. package/lib/yoke/adapters/antigravity.js +2 -1
  174. package/lib/yoke/adapters/claude-worker.js +11 -4
  175. package/lib/yoke/adapters/claude.js +73 -43
  176. package/lib/yoke/adapters/codex.js +40 -10
  177. package/lib/yoke/adapters/kiro.js +2 -1
  178. package/lib/yoke/claude-user-input.js +38 -0
  179. package/lib/yoke/codex-app-server.js +20 -2
  180. package/lib/yoke/codex-user-input.js +64 -0
  181. package/lib/yoke/complete-once.js +164 -0
  182. package/lib/yoke/index.js +5 -1
  183. package/lib/yoke/interface.js +19 -0
  184. package/lib/yoke/mcp-bridge-server.js +2 -6
  185. package/lib/yoke/user-input.js +254 -0
  186. package/package.json +3 -2
  187. package/lib/public/css/home-chat.css +0 -380
  188. package/lib/public/modules/home-chat.js +0 -438
  189. package/lib/server-clay-home.js +0 -245
@@ -0,0 +1,281 @@
1
+ // Vendor-aware Mate model chooser hosted by the centered Mate settings dialog.
2
+
3
+ import { getWs } from './ws-ref.js';
4
+
5
+ var activeMateId = null;
6
+ var activeMateName = "Mate";
7
+ var state = null;
8
+ var requestId = null;
9
+ var selectionRequestId = null;
10
+ var selectionModel = null;
11
+ var activeSessionId = null;
12
+ var requestSequence = 0;
13
+
14
+ function nextRequestId(prefix) {
15
+ requestSequence++;
16
+ return prefix + "-" + Date.now() + "-" + requestSequence;
17
+ }
18
+
19
+ function send(message) {
20
+ var ws = getWs();
21
+ if (!ws || ws.readyState !== 1) return false;
22
+ ws.send(JSON.stringify(message));
23
+ return true;
24
+ }
25
+
26
+ function modelValue(entry) {
27
+ if (!entry) return "";
28
+ if (typeof entry === "string") return entry;
29
+ return entry.value || entry.id || "";
30
+ }
31
+
32
+ function modelLabel(entry) {
33
+ if (typeof entry === "string") return entry;
34
+ return entry.displayName || entry.label || entry.name || modelValue(entry) || "Model";
35
+ }
36
+
37
+ function vendorLabel(vendor) {
38
+ var vendors = state && state.vendors ? state.vendors : [];
39
+ for (var i = 0; i < vendors.length; i++) {
40
+ if (vendors[i] && vendors[i].id === vendor) return vendors[i].displayName || vendor;
41
+ }
42
+ return vendor || "Vendor";
43
+ }
44
+
45
+ function findFocusTarget(kind, value) {
46
+ if (kind === "retry") return document.querySelector(".home-mate-settings-dialog .home-mate-model-retry");
47
+ if (kind === "selection-status") return document.querySelector(".home-mate-settings-dialog .home-mate-model-selection-status");
48
+ var selector = kind === "vendor" ? ".home-mate-settings-dialog [data-home-mate-vendor]" : ".home-mate-settings-dialog [data-home-mate-model]";
49
+ var datasetKey = kind === "vendor" ? "homeMateVendor" : "homeMateModel";
50
+ var options = document.querySelectorAll(selector);
51
+ for (var i = 0; i < options.length; i++) {
52
+ if (options[i].dataset[datasetKey] === value) return options[i];
53
+ }
54
+ return null;
55
+ }
56
+
57
+ function rerenderAndFocus(rerender, kind, value) {
58
+ rerender();
59
+ if (!kind) return;
60
+ var target = findFocusTarget(kind, value);
61
+ if (target) target.focus({ preventScroll: true });
62
+ }
63
+
64
+ export function resetHomeMateModelPicker(mateId, mateName, mate, sessionId) {
65
+ activeMateId = mateId;
66
+ activeMateName = mateName || "Mate";
67
+ requestId = null;
68
+ selectionRequestId = null;
69
+ selectionModel = null;
70
+ activeSessionId = sessionId || null;
71
+ state = {
72
+ status: "loading",
73
+ vendor: mate && mate.vendor ? mate.vendor : "",
74
+ model: mate && mate.model ? mate.model : "",
75
+ mateVendor: mate && mate.vendor ? mate.vendor : "",
76
+ mateModel: mate && mate.model ? mate.model : "",
77
+ vendors: [],
78
+ models: [],
79
+ error: "",
80
+ };
81
+ }
82
+
83
+ export function clearHomeMateModelPicker() {
84
+ activeMateId = null;
85
+ activeMateName = "Mate";
86
+ state = null;
87
+ requestId = null;
88
+ selectionRequestId = null;
89
+ selectionModel = null;
90
+ activeSessionId = null;
91
+ }
92
+
93
+ export function requestHomeMateModels(vendor, rerender, focusAfter) {
94
+ if (!activeMateId || !state || selectionRequestId) return;
95
+ var requestedVendor = vendor || state.vendor || state.mateVendor;
96
+ requestId = nextRequestId("home-mate-models");
97
+ state.vendor = requestedVendor;
98
+ state.status = "loading";
99
+ state.models = [];
100
+ state.error = "";
101
+ rerenderAndFocus(rerender, focusAfter ? "vendor" : "", requestedVendor);
102
+ if (send({ type: "home_mate_models_get", mateId: activeMateId, vendor: requestedVendor, requestId: requestId })) return;
103
+ state.status = "error";
104
+ state.error = "Clay is offline. Reconnect and try again.";
105
+ rerenderAndFocus(rerender, "retry", "");
106
+ }
107
+
108
+ function selectMateModel(model, rerender) {
109
+ if (!state || selectionRequestId || !model || !state.vendor) return;
110
+ selectionRequestId = nextRequestId("home-mate-model-set");
111
+ selectionModel = model;
112
+ state.error = "";
113
+ rerenderAndFocus(rerender, "selection-status", "");
114
+ var message = { type: "home_mate_model_set", mateId: activeMateId, vendor: state.vendor, model: model, requestId: selectionRequestId };
115
+ if (activeSessionId) message.sessionId = activeSessionId;
116
+ if (send(message)) return;
117
+ selectionRequestId = null;
118
+ selectionModel = null;
119
+ state.error = "Clay is offline. Reconnect and try again.";
120
+ rerenderAndFocus(rerender, "model", model);
121
+ }
122
+
123
+ function appendHeading(body, text) {
124
+ var heading = document.createElement("h3");
125
+ heading.className = "home-mate-model-section-label";
126
+ heading.textContent = text;
127
+ body.appendChild(heading);
128
+ }
129
+
130
+ function renderVendors(body, rerender) {
131
+ appendHeading(body, "Vendor");
132
+ var list = document.createElement("div");
133
+ list.className = "home-mate-vendor-list";
134
+ list.setAttribute("role", "group");
135
+ list.setAttribute("aria-label", "Vendor for " + activeMateName);
136
+ var vendors = state.vendors || [];
137
+ for (var i = 0; i < vendors.length; i++) {
138
+ (function (vendor) {
139
+ if (!vendor || !vendor.id) return;
140
+ var button = document.createElement("button");
141
+ button.type = "button";
142
+ button.className = "home-mate-vendor-option";
143
+ button.dataset.homeMateVendor = vendor.id;
144
+ button.textContent = vendor.displayName || vendor.id;
145
+ button.setAttribute("aria-pressed", vendor.id === state.vendor ? "true" : "false");
146
+ button.disabled = !!selectionRequestId;
147
+ if (vendor.id === state.mateVendor) button.title = "Current Mate vendor";
148
+ button.addEventListener("click", function () {
149
+ if (vendor.id !== state.vendor) requestHomeMateModels(vendor.id, rerender, true);
150
+ });
151
+ list.appendChild(button);
152
+ })(vendors[i]);
153
+ }
154
+ if (!vendors.length) {
155
+ var waiting = document.createElement("span");
156
+ waiting.className = "home-mate-model-muted";
157
+ waiting.textContent = "Loading available vendors…";
158
+ list.appendChild(waiting);
159
+ }
160
+ body.appendChild(list);
161
+ }
162
+
163
+ function appendLoadState(body, rerender) {
164
+ var loadState = document.createElement("div");
165
+ loadState.className = "home-mate-model-state";
166
+ if (state.status === "loading") {
167
+ loadState.classList.add("home-mate-model-loading");
168
+ loadState.setAttribute("role", "status");
169
+ loadState.dataset.homeModelFocus = "catalog-status";
170
+ loadState.tabIndex = -1;
171
+ loadState.textContent = "Loading " + vendorLabel(state.vendor) + " models…";
172
+ } else {
173
+ if (state.status === "error") loadState.setAttribute("role", "alert");
174
+ loadState.textContent = state.error || "No models are available for this vendor.";
175
+ var retry = document.createElement("button");
176
+ retry.type = "button";
177
+ retry.className = "home-mate-model-retry";
178
+ retry.dataset.homeModelFocus = "catalog-status";
179
+ retry.textContent = "Try again";
180
+ retry.addEventListener("click", function () { requestHomeMateModels(state.vendor, rerender, false); });
181
+ loadState.appendChild(retry);
182
+ }
183
+ body.appendChild(loadState);
184
+ }
185
+
186
+ function renderModels(body, rerender) {
187
+ appendHeading(body, "Model");
188
+ if (state.status !== "ready") {
189
+ appendLoadState(body, rerender);
190
+ return;
191
+ }
192
+ var list = document.createElement("div");
193
+ list.className = "home-mate-model-list";
194
+ list.setAttribute("role", "group");
195
+ list.setAttribute("aria-label", "Models from " + vendorLabel(state.vendor) + " for " + activeMateName);
196
+ var models = state.models || [];
197
+ for (var i = 0; i < models.length; i++) {
198
+ (function (entry) {
199
+ var value = modelValue(entry);
200
+ if (!value) return;
201
+ var button = document.createElement("button");
202
+ button.type = "button";
203
+ button.className = "home-mate-model-option";
204
+ button.dataset.homeMateModel = value;
205
+ button.setAttribute("aria-pressed", state.vendor === state.mateVendor && value === state.mateModel ? "true" : "false");
206
+ button.disabled = !!selectionRequestId;
207
+ var label = document.createElement("span");
208
+ label.textContent = modelLabel(entry);
209
+ var id = document.createElement("small");
210
+ id.textContent = value;
211
+ button.appendChild(label);
212
+ button.appendChild(id);
213
+ button.addEventListener("click", function () { selectMateModel(value, rerender); });
214
+ list.appendChild(button);
215
+ })(models[i]);
216
+ }
217
+ body.appendChild(list);
218
+ var status = document.createElement("div");
219
+ status.className = "home-mate-model-selection-status";
220
+ status.setAttribute("aria-live", "polite");
221
+ if (selectionRequestId) {
222
+ status.dataset.homeMateModel = selectionModel;
223
+ status.tabIndex = -1;
224
+ }
225
+ status.textContent = selectionRequestId ? "Saving vendor and model…" : (state.error || "");
226
+ body.appendChild(status);
227
+ }
228
+
229
+ export function renderHomeMateModelPicker(body, rerender) {
230
+ var note = document.createElement("p");
231
+ note.className = "home-mate-model-note";
232
+ note.textContent = activeSessionId ? "Used for this draft and future new conversations. Conversations with activity keep their committed vendor and model." : "Used for new conversations. Existing conversations keep their current vendor and model.";
233
+ body.appendChild(note);
234
+ if (!state) return;
235
+ if (state.mateVendor && state.mateModel) {
236
+ var current = document.createElement("p");
237
+ current.className = "home-mate-model-current";
238
+ current.textContent = "Current · " + vendorLabel(state.mateVendor) + " · " + state.mateModel;
239
+ body.appendChild(current);
240
+ }
241
+ renderVendors(body, rerender);
242
+ renderModels(body, rerender);
243
+ }
244
+
245
+ export function applyHomeMateModelsState(msg) {
246
+ if (!state || msg.mateId !== activeMateId || msg.requestId !== requestId) return false;
247
+ state.status = msg.status || "empty";
248
+ state.vendor = msg.vendor || state.vendor;
249
+ state.model = msg.model || "";
250
+ state.mateVendor = msg.mateVendor || state.mateVendor;
251
+ state.mateModel = msg.mateModel || state.mateModel;
252
+ state.vendors = Array.isArray(msg.vendors) ? msg.vendors : state.vendors;
253
+ state.models = Array.isArray(msg.models) ? msg.models : [];
254
+ state.error = msg.error || "";
255
+ return true;
256
+ }
257
+
258
+ export function applyHomeMateModelResult(msg) {
259
+ if (!state || msg.mateId !== activeMateId || msg.requestId !== selectionRequestId) return false;
260
+ selectionRequestId = null;
261
+ selectionModel = null;
262
+ if (!msg.ok) {
263
+ state.error = msg.error || "Could not save this vendor and model.";
264
+ return true;
265
+ }
266
+ state.mateVendor = msg.vendor || state.vendor;
267
+ state.mateModel = msg.model || state.model;
268
+ state.model = state.mateModel;
269
+ state.error = msg.requestedSessionId && !msg.sessionApplied ? (msg.sessionReason || "Mate default updated. This conversation keeps its committed model.") : "";
270
+ window.dispatchEvent(new CustomEvent("clay:home-mate-model-confirmed", { detail: {
271
+ mateId: msg.mateId,
272
+ vendor: state.mateVendor,
273
+ model: state.mateModel,
274
+ requestedSessionId: msg.requestedSessionId || null,
275
+ sessionId: msg.sessionId || null,
276
+ sessionApplied: msg.sessionApplied === true,
277
+ sessionVendor: msg.sessionVendor || null,
278
+ sessionModel: msg.sessionModel || null,
279
+ } }));
280
+ return true;
281
+ }
@@ -0,0 +1,38 @@
1
+ // Pure Home Mate selection policy shared by rendering and focused tests.
2
+
3
+ export function findHomeMate(mates, mateId) {
4
+ if (!mateId) return null;
5
+ for (var i = 0; i < mates.length; i++) {
6
+ if (mates[i] && mates[i].id === mateId) return mates[i];
7
+ }
8
+ return null;
9
+ }
10
+
11
+ export function resolveHomeMate(mates, currentMateId, preferredMateId) {
12
+ var visibleMates = Array.isArray(mates) ? mates : [];
13
+ var currentMate = findHomeMate(visibleMates, currentMateId);
14
+ if (currentMate) return currentMate;
15
+ var preferredMate = findHomeMate(visibleMates, preferredMateId);
16
+ if (preferredMate) return preferredMate;
17
+ for (var i = 0; i < visibleMates.length; i++) {
18
+ if (visibleMates[i] && visibleMates[i].builtinKey === "clay") return visibleMates[i];
19
+ }
20
+ return null;
21
+ }
22
+
23
+ export function getHomeMateName(mate) {
24
+ var profile = mate && mate.profile ? mate.profile : {};
25
+ return profile.displayName || (mate && (mate.displayName || mate.name)) || "Mate";
26
+ }
27
+
28
+ export function getHomeMateBio(mate) {
29
+ var profile = mate && mate.profile ? mate.profile : {};
30
+ return mate ? profile.bio || mate.bio || profile.description || mate.description || "" : "";
31
+ }
32
+
33
+ export function getHomeMateShortBio(mate) {
34
+ var bio = getHomeMateBio(mate).replace(/\s+/g, " ").trim();
35
+ if (!bio) return "Bring an idea, a task, or a question.";
36
+ if (bio.length > 120) return bio.slice(0, 117).trim() + "...";
37
+ return bio;
38
+ }
@@ -0,0 +1,131 @@
1
+ // Accessible row-local menu that opens the centered Mate settings dialog.
2
+
3
+ import { iconHtml, refreshIcons } from './icons.js';
4
+ import { openHomeMateSettings } from './home-mate-settings.js';
5
+
6
+ var menu = null;
7
+ var menuTrigger = null;
8
+
9
+ function getMateName(mate) {
10
+ var profile = mate && mate.profile ? mate.profile : {};
11
+ return profile.displayName || (mate && (mate.displayName || mate.name)) || "Mate";
12
+ }
13
+
14
+ function positionMenu() {
15
+ if (!menu || !menuTrigger || !menuTrigger.isConnected) return;
16
+ var rect = menuTrigger.getBoundingClientRect();
17
+ var menuRect = menu.getBoundingClientRect();
18
+ var left = Math.min(window.innerWidth - menuRect.width - 8, Math.max(8, rect.right - menuRect.width));
19
+ var top = rect.bottom + 5;
20
+ if (top + menuRect.height > window.innerHeight - 8) top = Math.max(8, rect.top - menuRect.height - 5);
21
+ menu.style.left = left + "px";
22
+ menu.style.top = top + "px";
23
+ }
24
+
25
+ function menuItems() {
26
+ return menu ? menu.querySelectorAll('[role="menuitem"]') : [];
27
+ }
28
+
29
+ function closeMenu(returnFocus) {
30
+ var trigger = menuTrigger;
31
+ if (menu) menu.remove();
32
+ if (trigger) trigger.setAttribute("aria-expanded", "false");
33
+ menu = null;
34
+ menuTrigger = null;
35
+ document.removeEventListener("pointerdown", handleMenuOutside, true);
36
+ document.removeEventListener("keydown", handleMenuKeydown, true);
37
+ window.removeEventListener("resize", closeMenuOnViewportChange);
38
+ if (returnFocus && trigger && trigger.isConnected) trigger.focus({ preventScroll: true });
39
+ }
40
+
41
+ function closeMenuOnViewportChange() {
42
+ closeMenu(false);
43
+ }
44
+
45
+ function handleMenuOutside(event) {
46
+ if (menu && !menu.contains(event.target) && event.target !== menuTrigger) closeMenu(false);
47
+ }
48
+
49
+ function handleMenuKeydown(event) {
50
+ if (!menu) return;
51
+ var items = menuItems();
52
+ if (event.key === "Escape") {
53
+ event.preventDefault();
54
+ event.stopPropagation();
55
+ closeMenu(true);
56
+ return;
57
+ }
58
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp" && event.key !== "Home" && event.key !== "End") return;
59
+ event.preventDefault();
60
+ var current = Array.prototype.indexOf.call(items, document.activeElement);
61
+ var next = 0;
62
+ if (event.key === "End") next = items.length - 1;
63
+ else if (event.key === "ArrowUp") next = current <= 0 ? items.length - 1 : current - 1;
64
+ else if (event.key === "ArrowDown") next = current < 0 || current === items.length - 1 ? 0 : current + 1;
65
+ if (items[next]) items[next].focus();
66
+ }
67
+
68
+ function openMenu(trigger, mate) {
69
+ closeMenu(false);
70
+ menuTrigger = trigger;
71
+ trigger.setAttribute("aria-expanded", "true");
72
+ menu = document.createElement("div");
73
+ menu.className = "home-mate-row-menu";
74
+ menu.setAttribute("role", "menu");
75
+ menu.setAttribute("aria-label", "Actions for " + getMateName(mate));
76
+ var settings = document.createElement("button");
77
+ settings.type = "button";
78
+ settings.className = "home-mate-row-menu-item";
79
+ settings.setAttribute("role", "menuitem");
80
+ settings.innerHTML = iconHtml("settings-2") + "<span>Mate settings</span>";
81
+ settings.addEventListener("click", function (event) {
82
+ event.stopPropagation();
83
+ var opener = trigger;
84
+ closeMenu(false);
85
+ openHomeMateSettings(mate.id, opener);
86
+ });
87
+ menu.appendChild(settings);
88
+ document.body.appendChild(menu);
89
+ refreshIcons();
90
+ requestAnimationFrame(function () {
91
+ positionMenu();
92
+ settings.focus({ preventScroll: true });
93
+ });
94
+ document.addEventListener("pointerdown", handleMenuOutside, true);
95
+ document.addEventListener("keydown", handleMenuKeydown, true);
96
+ window.addEventListener("resize", closeMenuOnViewportChange);
97
+ }
98
+
99
+ export function createHomeMateSettingsTrigger(mate) {
100
+ var trigger = document.createElement("button");
101
+ trigger.type = "button";
102
+ trigger.className = "home-mate-list-overflow";
103
+ trigger.dataset.homeMateId = mate.id;
104
+ trigger.setAttribute("aria-label", "Actions for " + getMateName(mate));
105
+ trigger.setAttribute("title", "Mate actions");
106
+ trigger.setAttribute("aria-haspopup", "menu");
107
+ trigger.setAttribute("aria-expanded", "false");
108
+ trigger.innerHTML = '<span class="home-mate-list-overflow-mark" aria-hidden="true">•••</span>';
109
+ trigger.addEventListener("click", function (event) {
110
+ event.preventDefault();
111
+ event.stopPropagation();
112
+ if (menu && menuTrigger === trigger) closeMenu(false);
113
+ else openMenu(trigger, mate);
114
+ });
115
+ trigger.addEventListener("keydown", function (event) {
116
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp" && event.key !== "Home" && event.key !== "End") return;
117
+ event.preventDefault();
118
+ event.stopPropagation();
119
+ openMenu(trigger, mate);
120
+ requestAnimationFrame(function () {
121
+ var items = menuItems();
122
+ if (!items.length) return;
123
+ items[event.key === "ArrowUp" || event.key === "End" ? items.length - 1 : 0].focus();
124
+ });
125
+ });
126
+ return trigger;
127
+ }
128
+
129
+ export function disposeHomeMateSettingsMenu() {
130
+ closeMenu(false);
131
+ }