clay-server 5.2.0-beta.3 → 5.2.0-beta.5

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 (339) hide show
  1. package/lib/account-auth-socket.js +1 -0
  2. package/lib/builtin-mates.js +19 -16
  3. package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
  4. package/lib/canonical-clay-record-authority.js +15 -0
  5. package/lib/canonical-clay-scheduling-authority.js +19 -0
  6. package/lib/claude-hook-installer.js +28 -0
  7. package/lib/clay-mate-chat-receipts.js +26 -0
  8. package/lib/clay-mate-chat.js +87 -0
  9. package/lib/clay-mate-knowledge-input.js +26 -0
  10. package/lib/clay-mate-knowledge.js +95 -0
  11. package/lib/clay-mate-lifecycle.js +95 -0
  12. package/lib/clay-product-control-input.js +33 -0
  13. package/lib/clay-product-control-tools.js +51 -0
  14. package/lib/clay-product-control.js +237 -0
  15. package/lib/clay-project-lifecycle.js +154 -0
  16. package/lib/clay-project-record-input.js +44 -0
  17. package/lib/clay-project-records.js +83 -0
  18. package/lib/clay-scheduling-input.js +21 -0
  19. package/lib/clay-scheduling-receipts.js +26 -0
  20. package/lib/clay-scheduling.js +119 -0
  21. package/lib/clay-screen-controls.js +96 -0
  22. package/lib/clay-workspace-controls.js +83 -0
  23. package/lib/clay-workspace-input.js +17 -0
  24. package/lib/clay-workspace-receipts.js +26 -0
  25. package/lib/daemon-project-access.js +11 -0
  26. package/lib/daemon-project-lifecycle-callbacks.js +15 -0
  27. package/lib/daemon-project-lifecycle.js +262 -0
  28. package/lib/daemon-projects.js +1 -1
  29. package/lib/daemon.js +73 -221
  30. package/lib/issues-service.js +29 -15
  31. package/lib/knowledge-db-import.js +5 -4
  32. package/lib/knowledge-db-service.js +9 -6
  33. package/lib/knowledge-document-index.js +2 -1
  34. package/lib/knowledge-file-store.js +7 -1
  35. package/lib/knowledge-product-operations.js +46 -0
  36. package/lib/mate-avatar-catalog.js +12 -0
  37. package/lib/mate-avatar-migration.js +41 -0
  38. package/lib/mate-creation-mcp-server.js +2 -0
  39. package/lib/mate-knowledge-canonical-access.js +10 -0
  40. package/lib/mate-knowledge-service.js +7 -4
  41. package/lib/mate-lifecycle-service.js +94 -0
  42. package/lib/mate-message-delivery.js +43 -0
  43. package/lib/mate-ready-creation.js +9 -1
  44. package/lib/mate-schedule-input.js +29 -0
  45. package/lib/mate-schedule-lifecycle.js +151 -0
  46. package/lib/mate-scheduling.js +103 -0
  47. package/lib/mates.js +18 -6
  48. package/lib/mcp-agent-tools.js +117 -0
  49. package/lib/mcp-connection-store.js +119 -15
  50. package/lib/mcp-import-credentials.js +31 -0
  51. package/lib/mcp-local-transport.js +13 -0
  52. package/lib/mcp-oauth.js +8 -6
  53. package/lib/mcp-query-bridge.js +30 -0
  54. package/lib/mcp-remote-connections.js +74 -20
  55. package/lib/mcp-source-adapters.js +91 -0
  56. package/lib/mcp-source-context.js +42 -0
  57. package/lib/mcp-source-discovery.js +169 -0
  58. package/lib/mcp-source-plugins.js +68 -0
  59. package/lib/mcp-source-restrictions.js +47 -0
  60. package/lib/mcp-tool-policy.js +34 -0
  61. package/lib/notes-checklist.js +50 -0
  62. package/lib/notes.js +49 -3
  63. package/lib/os-users.js +20 -5
  64. package/lib/project-admin-overview.js +51 -0
  65. package/lib/project-ask-user.js +8 -4
  66. package/lib/project-debate.js +1 -0
  67. package/lib/project-delegated-follow-up.js +2 -1
  68. package/lib/project-directory-claim.js +65 -0
  69. package/lib/project-file-operations.js +61 -0
  70. package/lib/project-filesystem.js +13 -87
  71. package/lib/project-full-access.js +23 -2
  72. package/lib/project-knowledge.js +32 -18
  73. package/lib/project-lifecycle-operations.js +98 -0
  74. package/lib/project-lifecycle-process-witness.js +32 -0
  75. package/lib/project-lifecycle-runtime.js +50 -0
  76. package/lib/project-linux-access.js +22 -0
  77. package/lib/project-logs-canonical-clay.js +27 -0
  78. package/lib/project-logs-clay-query.js +16 -0
  79. package/lib/project-logs-schema.js +4 -2
  80. package/lib/project-logs-service.js +8 -7
  81. package/lib/project-logs.js +1 -0
  82. package/lib/project-loop.js +1 -0
  83. package/lib/project-mate-chat-control.js +55 -0
  84. package/lib/project-mate-creation-proposal.js +18 -3
  85. package/lib/project-mate-interaction.js +8 -0
  86. package/lib/project-mcp.js +54 -16
  87. package/lib/project-models.js +14 -31
  88. package/lib/project-notifications.js +74 -16
  89. package/lib/project-ownership.js +77 -0
  90. package/lib/project-pair-result-delivery.js +14 -3
  91. package/lib/project-pending-message-queue.js +1 -0
  92. package/lib/project-product-control.js +87 -0
  93. package/lib/project-product-session-queue.js +46 -0
  94. package/lib/project-push-access.js +28 -0
  95. package/lib/project-scheduled-message-queue.js +30 -0
  96. package/lib/project-scheduled-messages.js +63 -64
  97. package/lib/project-scheduled-task-control.js +83 -0
  98. package/lib/project-scheduled-tasks.js +13 -2
  99. package/lib/project-scheduling-access.js +10 -0
  100. package/lib/project-session-actions.js +65 -0
  101. package/lib/project-session-delete.js +8 -1
  102. package/lib/project-session-document.js +8 -3
  103. package/lib/project-session-github.js +63 -19
  104. package/lib/project-session-history-actions.js +165 -0
  105. package/lib/project-session-linear.js +14 -6
  106. package/lib/project-session-notes.js +7 -3
  107. package/lib/project-session-runtime-actions.js +63 -0
  108. package/lib/project-session-vendor.js +54 -0
  109. package/lib/project-sessions.js +68 -241
  110. package/lib/project-shared-browser.js +56 -12
  111. package/lib/project-user-message.js +30 -1
  112. package/lib/project-workspace-operations.js +126 -0
  113. package/lib/project-workspace-query.js +9 -2
  114. package/lib/project.js +144 -37
  115. package/lib/public/app.js +5 -0
  116. package/lib/public/appearance-bootstrap.js +14 -0
  117. package/lib/public/css/clay-tool-activity.css +51 -0
  118. package/lib/public/css/clay-workspace.css +29 -0
  119. package/lib/public/css/home-debate-planning.css +2 -0
  120. package/lib/public/css/home-mate-creation.css +5 -0
  121. package/lib/public/css/mate-avatar-gallery.css +12 -0
  122. package/lib/public/css/mate-conversation.css +10 -5
  123. package/lib/public/css/overlays.css +4 -2
  124. package/lib/public/css/project-admin-overview.css +13 -0
  125. package/lib/public/css/session-folders.css +11 -0
  126. package/lib/public/css/session-github.css +46 -1
  127. package/lib/public/css/shared-browser.css +35 -1
  128. package/lib/public/css/sticky-notes.css +61 -1
  129. package/lib/public/index.html +11 -5
  130. package/lib/public/mate-avatars/B01.png +0 -0
  131. package/lib/public/mate-avatars/B02.png +0 -0
  132. package/lib/public/mate-avatars/B03.png +0 -0
  133. package/lib/public/mate-avatars/B04.png +0 -0
  134. package/lib/public/mate-avatars/B05.png +0 -0
  135. package/lib/public/mate-avatars/B06.png +0 -0
  136. package/lib/public/mate-avatars/B07.png +0 -0
  137. package/lib/public/mate-avatars/B08.png +0 -0
  138. package/lib/public/mate-avatars/B09.png +0 -0
  139. package/lib/public/mate-avatars/B10.png +0 -0
  140. package/lib/public/mate-avatars/B11.png +0 -0
  141. package/lib/public/mate-avatars/B12.png +0 -0
  142. package/lib/public/mate-avatars/B13.png +0 -0
  143. package/lib/public/mate-avatars/B14.png +0 -0
  144. package/lib/public/mate-avatars/B15.png +0 -0
  145. package/lib/public/mate-avatars/B16.png +0 -0
  146. package/lib/public/mate-avatars/B17.png +0 -0
  147. package/lib/public/mate-avatars/B18.png +0 -0
  148. package/lib/public/mate-avatars/B19.png +0 -0
  149. package/lib/public/mate-avatars/B20.png +0 -0
  150. package/lib/public/mate-avatars/B21.png +0 -0
  151. package/lib/public/mate-avatars/B22.png +0 -0
  152. package/lib/public/mate-avatars/B23.png +0 -0
  153. package/lib/public/mate-avatars/B24.png +0 -0
  154. package/lib/public/mate-avatars/B25.png +0 -0
  155. package/lib/public/mate-avatars/B26.png +0 -0
  156. package/lib/public/mate-avatars/B27.png +0 -0
  157. package/lib/public/mate-avatars/B28.png +0 -0
  158. package/lib/public/mate-avatars/B29.png +0 -0
  159. package/lib/public/mate-avatars/B30.png +0 -0
  160. package/lib/public/mate-avatars/B31.png +0 -0
  161. package/lib/public/mate-avatars/B32.png +0 -0
  162. package/lib/public/mate-avatars/B33.png +0 -0
  163. package/lib/public/mate-avatars/B34.png +0 -0
  164. package/lib/public/mate-avatars/B35.png +0 -0
  165. package/lib/public/mate-avatars/B36.png +0 -0
  166. package/lib/public/mate-avatars/B37.png +0 -0
  167. package/lib/public/mate-avatars/B38.png +0 -0
  168. package/lib/public/mate-avatars/B39.png +0 -0
  169. package/lib/public/mate-avatars/B40.png +0 -0
  170. package/lib/public/mate-avatars/B41.png +0 -0
  171. package/lib/public/mate-avatars/B42.png +0 -0
  172. package/lib/public/mate-avatars/B43.png +0 -0
  173. package/lib/public/mate-avatars/B44.png +0 -0
  174. package/lib/public/mate-avatars/B45.png +0 -0
  175. package/lib/public/mate-avatars/B46.png +0 -0
  176. package/lib/public/mate-avatars/B47.png +0 -0
  177. package/lib/public/mate-avatars/B48.png +0 -0
  178. package/lib/public/mate-avatars/C01.png +0 -0
  179. package/lib/public/mate-avatars/C02.png +0 -0
  180. package/lib/public/mate-avatars/C03.png +0 -0
  181. package/lib/public/mate-avatars/C04.png +0 -0
  182. package/lib/public/mate-avatars/C05.png +0 -0
  183. package/lib/public/mate-avatars/C06.png +0 -0
  184. package/lib/public/mate-avatars/C07.png +0 -0
  185. package/lib/public/mate-avatars/C08.png +0 -0
  186. package/lib/public/mate-avatars/C09.png +0 -0
  187. package/lib/public/mate-avatars/C10.png +0 -0
  188. package/lib/public/mate-avatars/C11.png +0 -0
  189. package/lib/public/mate-avatars/C12.png +0 -0
  190. package/lib/public/mate-avatars/C13.png +0 -0
  191. package/lib/public/mate-avatars/C14.png +0 -0
  192. package/lib/public/mate-avatars/C15.png +0 -0
  193. package/lib/public/mate-avatars/C16.png +0 -0
  194. package/lib/public/mate-avatars/C17.png +0 -0
  195. package/lib/public/mate-avatars/C18.png +0 -0
  196. package/lib/public/mate-avatars/C19.png +0 -0
  197. package/lib/public/mate-avatars/C20.png +0 -0
  198. package/lib/public/mate-avatars/C21.png +0 -0
  199. package/lib/public/mate-avatars/C22.png +0 -0
  200. package/lib/public/mate-avatars/C23.png +0 -0
  201. package/lib/public/mate-avatars/C24.png +0 -0
  202. package/lib/public/mate-avatars/C25.png +0 -0
  203. package/lib/public/mate-avatars/C26.png +0 -0
  204. package/lib/public/mate-avatars/C27.png +0 -0
  205. package/lib/public/mate-avatars/C28.png +0 -0
  206. package/lib/public/mate-avatars/C29.png +0 -0
  207. package/lib/public/mate-avatars/C30.png +0 -0
  208. package/lib/public/mate-avatars/C31.png +0 -0
  209. package/lib/public/mate-avatars/C32.png +0 -0
  210. package/lib/public/mate-avatars/C33.png +0 -0
  211. package/lib/public/mate-avatars/C34.png +0 -0
  212. package/lib/public/mate-avatars/C35.png +0 -0
  213. package/lib/public/mate-avatars/C36.png +0 -0
  214. package/lib/public/mate-avatars/C37.png +0 -0
  215. package/lib/public/mate-avatars/C38.png +0 -0
  216. package/lib/public/mate-avatars/C39.png +0 -0
  217. package/lib/public/mate-avatars/C40.png +0 -0
  218. package/lib/public/mate-avatars/C41.png +0 -0
  219. package/lib/public/mate-avatars/C42.png +0 -0
  220. package/lib/public/mate-avatars/C43.png +0 -0
  221. package/lib/public/mate-avatars/C44.png +0 -0
  222. package/lib/public/mate-avatars/C45.png +0 -0
  223. package/lib/public/mate-avatars/C46.png +0 -0
  224. package/lib/public/mate-avatars/C47.png +0 -0
  225. package/lib/public/mate-avatars/C48.png +0 -0
  226. package/lib/public/mate-avatars/catalog.json +482 -0
  227. package/lib/public/mate-avatars/lead-clay.png +0 -0
  228. package/lib/public/modules/admin-settings-projects.js +6 -2
  229. package/lib/public/modules/app-connection.js +8 -1
  230. package/lib/public/modules/app-message-router.js +4 -0
  231. package/lib/public/modules/app-messages.js +31 -5
  232. package/lib/public/modules/app-projects.js +5 -0
  233. package/lib/public/modules/app-rendering.js +1 -1
  234. package/lib/public/modules/avatar.js +15 -41
  235. package/lib/public/modules/chat-render-runtime.js +10 -1
  236. package/lib/public/modules/clay-browser-links.js +47 -0
  237. package/lib/public/modules/clay-log-links.js +1 -1
  238. package/lib/public/modules/clay-owner-navigation.js +27 -0
  239. package/lib/public/modules/clay-tool-activity.js +140 -0
  240. package/lib/public/modules/clay-tool-presentation.js +338 -0
  241. package/lib/public/modules/command-palette.js +2 -2
  242. package/lib/public/modules/debate.js +1 -0
  243. package/lib/public/modules/default-ai.js +11 -3
  244. package/lib/public/modules/history-replay-batch.js +5 -1
  245. package/lib/public/modules/home-debate-live.js +2 -0
  246. package/lib/public/modules/home-debate-planning.js +7 -7
  247. package/lib/public/modules/home-mate-avatar-editor.js +127 -208
  248. package/lib/public/modules/home-mate-chat.js +8 -6
  249. package/lib/public/modules/home-mate-creation.js +73 -1
  250. package/lib/public/modules/home-mate-settings.js +19 -12
  251. package/lib/public/modules/markdown.js +4 -0
  252. package/lib/public/modules/mate-avatar-catalog.js +97 -0
  253. package/lib/public/modules/mate-management.js +40 -7
  254. package/lib/public/modules/mcp-connection-view.js +31 -13
  255. package/lib/public/modules/mcp-discovery-view.js +31 -0
  256. package/lib/public/modules/mcp-ui.js +23 -4
  257. package/lib/public/modules/mention.js +3 -0
  258. package/lib/public/modules/pane-browser-bridge.js +20 -0
  259. package/lib/public/modules/personal-settings-dialog.js +7 -1
  260. package/lib/public/modules/profile.js +1 -238
  261. package/lib/public/modules/project-access-recovery.js +17 -0
  262. package/lib/public/modules/project-admin-overview.js +21 -0
  263. package/lib/public/modules/project-mate-navigation.js +99 -14
  264. package/lib/public/modules/search-clay-chat.js +16 -2
  265. package/lib/public/modules/session-folder-layout.js +1 -1
  266. package/lib/public/modules/session-folder-view-menu.js +9 -0
  267. package/lib/public/modules/session-github.js +85 -14
  268. package/lib/public/modules/session-work-primary.js +51 -0
  269. package/lib/public/modules/shared-browser-settings.js +112 -0
  270. package/lib/public/modules/shared-browser-tabs.js +13 -3
  271. package/lib/public/modules/shared-browser.js +27 -6
  272. package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
  273. package/lib/public/modules/split-view.js +2 -0
  274. package/lib/public/modules/sticky-note-markdown.js +72 -6
  275. package/lib/public/modules/sticky-notes-actions.js +30 -0
  276. package/lib/public/modules/sticky-notes-browser.js +9 -2
  277. package/lib/public/modules/sticky-notes-card.js +23 -7
  278. package/lib/public/modules/sticky-notes-checklist.js +71 -0
  279. package/lib/public/modules/sticky-notes-editor.js +108 -44
  280. package/lib/public/modules/sticky-notes-shared.js +50 -2
  281. package/lib/public/modules/sticky-notes-state.js +66 -0
  282. package/lib/public/modules/sticky-notes.js +34 -51
  283. package/lib/public/modules/theme.js +8 -1
  284. package/lib/public/modules/thinking-lifecycle.js +33 -3
  285. package/lib/public/modules/thinking-view.js +23 -1
  286. package/lib/public/modules/tools.js +36 -7
  287. package/lib/public/modules/user-settings-appearance.js +10 -3
  288. package/lib/public/modules/user-settings-editors.js +1 -1
  289. package/lib/public/modules/user-settings-layout.js +11 -2
  290. package/lib/public/modules/user-settings-preferences.js +6 -2
  291. package/lib/public/modules/user-settings-profile.js +12 -2
  292. package/lib/public/modules/user-settings.js +3 -2
  293. package/lib/public/style.css +3 -0
  294. package/lib/public/sw.js +3 -1
  295. package/lib/scheduled-task-records.js +3 -1
  296. package/lib/scheduler.js +2 -0
  297. package/lib/sdk-bridge.js +228 -23
  298. package/lib/sdk-message-processor.js +2 -0
  299. package/lib/sdk-response-recovery.js +155 -0
  300. package/lib/server-admin.js +63 -16
  301. package/lib/server-appearance-settings.js +13 -1
  302. package/lib/server-dm.js +2 -1
  303. package/lib/server-global-ws.js +10 -20
  304. package/lib/server-home-chat-events.js +18 -4
  305. package/lib/server-home-chat.js +12 -16
  306. package/lib/server-home-mate-creation.js +1 -0
  307. package/lib/server-home-mcp-consent.js +14 -0
  308. package/lib/server-mates.js +25 -62
  309. package/lib/server-owner-ui.js +77 -0
  310. package/lib/server-path-access.js +38 -0
  311. package/lib/server-project-removal.js +22 -0
  312. package/lib/server-settings.js +4 -1
  313. package/lib/server.js +144 -41
  314. package/lib/session-config-transition.js +45 -0
  315. package/lib/session-folders.js +7 -2
  316. package/lib/session-github.js +9 -2
  317. package/lib/session-visibility.js +5 -2
  318. package/lib/session-work-primary.js +50 -0
  319. package/lib/sessions.js +13 -0
  320. package/lib/shared-browser-cookie-sync.js +32 -0
  321. package/lib/shared-browser-cookies.js +79 -0
  322. package/lib/shared-browser-runtime.js +25 -9
  323. package/lib/shared-browser-storage-sync.js +43 -0
  324. package/lib/shared-browser-storage.js +101 -0
  325. package/lib/shared-browser-tools.js +2 -1
  326. package/lib/shared-browser-worker.js +46 -10
  327. package/lib/terminal-manager.js +52 -0
  328. package/lib/users-permissions.js +2 -8
  329. package/lib/users.js +4 -0
  330. package/lib/workspace-query-access.js +4 -1
  331. package/lib/ws-schema.js +8 -2
  332. package/lib/yoke/acp-agent-profiles.js +2 -2
  333. package/lib/yoke/adapters/claude.js +47 -8
  334. package/lib/yoke/adapters/codex.js +52 -111
  335. package/lib/yoke/claude-worker-startup.js +28 -0
  336. package/lib/yoke/codex-idle-recovery.js +81 -1
  337. package/lib/yoke/codex-recovery-admission.js +164 -0
  338. package/lib/yoke/session-tool-transport.js +3 -0
  339. package/package.json +5 -1
@@ -4,7 +4,7 @@ import { setChatLayout } from './theme.js';
4
4
  // Generated identities use deterministic local pixel identicons.
5
5
 
6
6
  import { iconHtml, refreshIcons } from './icons.js';
7
- import { avatarUrl, mateAvatarUrl, mateMarkUrl, userAvatarSeed } from './avatar.js';
7
+ import { avatarUrl, userAvatarSeed } from './avatar.js';
8
8
  import { store } from './store.js';
9
9
  import { applyTerminalFont } from './terminal-prefs.js';
10
10
 
@@ -586,240 +586,3 @@ export function getProfile() {
586
586
  export function getProfileLang() {
587
587
  return profile.lang;
588
588
  }
589
-
590
- // --- Mate profile popover (reuses same UI minus language) ---
591
- var matePopoverEl = null;
592
- var mateSaveTimer = null;
593
-
594
- export function showMateProfilePopover(anchorEl, mateData, onUpdate) {
595
- if (matePopoverEl) {
596
- hideMatePopover();
597
- return;
598
- }
599
-
600
- var mp = mateData.profile || {};
601
- var mateName = mp.displayName || mateData.name || '';
602
- var mateColor = mp.avatarColor || '#5857fc';
603
- var mateStyle = mp.avatarStyle || 'imprint';
604
- var mateSeed = mp.avatarSeed || mateData.id || 'mate';
605
- var mateCustom = mp.avatarCustom || '';
606
-
607
- matePopoverEl = document.createElement('div');
608
- matePopoverEl.className = 'profile-popover mate-profile-popover';
609
-
610
- var html = '';
611
-
612
- // Banner + close
613
- html += '<div class="profile-banner">';
614
- html += '<button class="profile-close-btn">&times;</button>';
615
- html += '</div>';
616
-
617
- // Avatar row
618
- var mateAvatarSrc = mateCustom ? mateCustom : mateMarkUrl(mateName || 'Mate', 80);
619
- html += '<div class="profile-avatar-row">';
620
- html += '<div class="profile-popover-avatar">';
621
- html += '<img class="profile-popover-avatar-img" src="' + mateAvatarSrc + '" alt="' + escapeAttr(mateName || 'Clay Mate') + '">';
622
- html += '</div>';
623
- html += '<div class="profile-name-display">' + escapeAttr(mateName || 'New Mate') + '</div>';
624
- html += '</div>';
625
-
626
- // Body
627
- html += '<div class="profile-popover-body">';
628
-
629
- // Name
630
- html += '<div class="profile-field">';
631
- html += '<label class="profile-field-label">Display Name</label>';
632
- html += '<input type="text" class="profile-field-input" id="mate-profile-name" value="' + escapeAttr(mateName) + '" placeholder="Name your mate..." maxlength="50" spellcheck="false" autocomplete="off">';
633
- html += '</div>';
634
-
635
- // Avatar picker (hidden if avatar is locked)
636
- var mateAvatarLocked = mp.avatarLocked || false;
637
- if (!mateAvatarLocked) {
638
- html += '<div class="profile-field">';
639
- html += '<label class="profile-field-label">Identity</label>';
640
- html += '<div class="profile-imprint-panel">';
641
- html += '<div class="profile-imprint-preview"><img src="' + mateMarkUrl(mateName || 'Mate', 72) + '" alt="Clay Mate initial"><span><strong>Mate initial</strong><small>Based on display name</small></span></div>';
642
- html += '<div class="profile-imprint-actions profile-mate-mark-actions">';
643
- html += '<button class="profile-use-imprint' + (!mateCustom ? ' is-active' : '') + '" type="button">Use initial</button>';
644
- html += '<button class="profile-avatar-upload' + (mateCustom ? ' is-active' : '') + '" type="button">Upload image</button>';
645
- html += '</div>';
646
- html += '</div>';
647
- html += '<input type="file" id="mate-profile-avatar-file" accept="image/*" style="display:none">';
648
- html += '</div>';
649
- }
650
-
651
- html += '</div>'; // close body
652
-
653
- matePopoverEl.innerHTML = html;
654
-
655
- // State tracker
656
- var mateProfile = {
657
- displayName: mateName,
658
- avatarStyle: mateStyle,
659
- avatarSeed: mateSeed,
660
- avatarColor: mateColor,
661
- avatarCustom: mateCustom,
662
- };
663
-
664
- function refreshChatAvatars() {
665
- var newUrl = mateAvatarUrl({ profile: mateProfile, id: mateData.id }, 64);
666
- // Update all mate avatars in the chat area
667
- var bubbles = document.querySelectorAll('.dm-bubble-avatar-mate');
668
- for (var i = 0; i < bubbles.length; i++) {
669
- bubbles[i].src = newUrl;
670
- }
671
- // Update body dataset so new messages use the updated URL
672
- if (document.body.dataset.mateAvatarUrl) {
673
- document.body.dataset.mateAvatarUrl = newUrl;
674
- }
675
- // Update mate sidebar avatar
676
- var sidebarAvatar = document.querySelector('.mate-sidebar-avatar');
677
- if (sidebarAvatar) sidebarAvatar.src = newUrl;
678
- }
679
-
680
- function debouncedMateUpdate() {
681
- if (mateSaveTimer) clearTimeout(mateSaveTimer);
682
- mateSaveTimer = setTimeout(function() {
683
- if (onUpdate) onUpdate({
684
- name: mateProfile.displayName,
685
- profile: {
686
- displayName: mateProfile.displayName,
687
- avatarStyle: mateProfile.avatarStyle,
688
- avatarSeed: mateProfile.avatarSeed,
689
- avatarColor: mateProfile.avatarColor,
690
- avatarCustom: mateProfile.avatarCustom,
691
- },
692
- });
693
- refreshChatAvatars();
694
- mateSaveTimer = null;
695
- }, 400);
696
- }
697
-
698
- function updateMatePopoverHeader() {
699
- if (!matePopoverEl) return;
700
- var img = matePopoverEl.querySelector('.profile-popover-avatar-img');
701
- var nd = matePopoverEl.querySelector('.profile-name-display');
702
- if (img) img.src = mateProfile.avatarCustom
703
- ? mateProfile.avatarCustom
704
- : mateMarkUrl(mateProfile.displayName || 'Mate', 80);
705
- var preview = matePopoverEl.querySelector('.profile-imprint-preview img');
706
- if (preview) preview.src = mateMarkUrl(mateProfile.displayName || 'Mate', 72);
707
- if (nd) nd.textContent = mateProfile.displayName || 'New Mate';
708
- }
709
-
710
- // Events
711
- matePopoverEl.querySelector('.profile-close-btn').addEventListener('click', function(e) {
712
- e.stopPropagation();
713
- hideMatePopover();
714
- });
715
-
716
- var nameInput = matePopoverEl.querySelector('#mate-profile-name');
717
- nameInput.addEventListener('input', function() {
718
- mateProfile.displayName = nameInput.value.trim();
719
- updateMatePopoverHeader();
720
- debouncedMateUpdate();
721
- });
722
- nameInput.addEventListener('keydown', function(e) {
723
- if (e.key === 'Enter') { e.preventDefault(); hideMatePopover(); }
724
- e.stopPropagation();
725
- });
726
- nameInput.addEventListener('keyup', function(e) { e.stopPropagation(); });
727
- nameInput.addEventListener('keypress', function(e) { e.stopPropagation(); });
728
-
729
- // Avatar upload button
730
- var mateUploadBtn = matePopoverEl.querySelector('.profile-avatar-upload');
731
- var mateFileInput = matePopoverEl.querySelector('#mate-profile-avatar-file');
732
- if (mateUploadBtn && mateFileInput) {
733
- mateUploadBtn.addEventListener('click', function() {
734
- mateFileInput.click();
735
- });
736
- mateFileInput.addEventListener('change', function() {
737
- var file = mateFileInput.files[0];
738
- if (!file) return;
739
-
740
- function uploadMateBlob(blob) {
741
- blob.arrayBuffer().then(function(ab) {
742
- var buf = new Uint8Array(ab);
743
- fetch('/api/mate-avatar/' + encodeURIComponent(mateData.id), {
744
- method: 'POST',
745
- headers: { 'Content-Type': 'application/octet-stream' },
746
- body: buf,
747
- }).then(function(r) { return r.json(); }).then(function(data) {
748
- if (data.ok) {
749
- mateProfile.avatarCustom = data.avatar;
750
- updateMatePopoverHeader();
751
- if (mateUploadBtn) {
752
- mateUploadBtn.classList.add('is-active');
753
- }
754
- var imprintButton = matePopoverEl && matePopoverEl.querySelector('.profile-use-imprint');
755
- if (imprintButton) imprintButton.classList.remove('is-active');
756
- debouncedMateUpdate();
757
- }
758
- });
759
- });
760
- }
761
-
762
- // Load image and open position picker
763
- var img = new Image();
764
- var objectUrl = URL.createObjectURL(file);
765
- img.onload = function() {
766
- showAvatarPositioner(img, objectUrl, function(croppedBlob) {
767
- URL.revokeObjectURL(objectUrl);
768
- uploadMateBlob(croppedBlob);
769
- });
770
- };
771
- img.src = objectUrl;
772
- });
773
- }
774
-
775
- if (!mateAvatarLocked) {
776
- matePopoverEl.querySelector('.profile-use-imprint').addEventListener('click', function() {
777
- mateProfile.avatarCustom = '';
778
- updateMatePopoverHeader();
779
- matePopoverEl.querySelector('.profile-use-imprint').classList.add('is-active');
780
- matePopoverEl.querySelector('.profile-avatar-upload').classList.remove('is-active');
781
- debouncedMateUpdate();
782
- });
783
- }
784
-
785
- matePopoverEl.addEventListener('click', function(e) { e.stopPropagation(); });
786
-
787
- // Position near anchor
788
- document.body.appendChild(matePopoverEl);
789
- refreshIcons();
790
-
791
- var rect = anchorEl.getBoundingClientRect();
792
- matePopoverEl.style.position = 'fixed';
793
- matePopoverEl.style.left = (rect.right + 8) + 'px';
794
- matePopoverEl.style.zIndex = '9999';
795
- // Align bottom of popover with bottom of anchor icon
796
- var popHeight = matePopoverEl.offsetHeight;
797
- var bottomAligned = rect.bottom - popHeight;
798
- matePopoverEl.style.top = Math.max(8, bottomAligned) + 'px';
799
-
800
- setTimeout(function() {
801
- document.addEventListener('click', closeMateOnOutside);
802
- document.addEventListener('keydown', closeMateOnEscape);
803
- }, 0);
804
- }
805
-
806
- function closeMateOnOutside(e) {
807
- if (matePopoverEl && !matePopoverEl.contains(e.target)) {
808
- hideMatePopover();
809
- }
810
- }
811
-
812
- function closeMateOnEscape(e) {
813
- if (e.key === 'Escape' && matePopoverEl) {
814
- hideMatePopover();
815
- }
816
- }
817
-
818
- function hideMatePopover() {
819
- if (matePopoverEl) {
820
- matePopoverEl.remove();
821
- matePopoverEl = null;
822
- }
823
- document.removeEventListener('click', closeMateOnOutside);
824
- document.removeEventListener('keydown', closeMateOnEscape);
825
- }
@@ -0,0 +1,17 @@
1
+ import { store } from './store.js';
2
+
3
+ // Hard navigation destroys detached panels, cached transcripts and sockets.
4
+ // Clear the rendered document first so revoked content cannot linger while the
5
+ // replacement page is loading or when that request fails.
6
+ export function leaveRevokedProject(url) {
7
+ store.set({ activeSessionId: null, activeProjectSlug: null, sessionActivatedProjectSlug: null, sessions: [], splitPanes: null });
8
+ document.body.replaceChildren();
9
+ window.location.replace(url || "/");
10
+ }
11
+
12
+ export function reconnectDestination(info, slug) {
13
+ if (!slug || !info || !Array.isArray(info.projects)) return null;
14
+ var project = info.projects.find(function (item) { return item.slug === slug; });
15
+ if (!project) return "/";
16
+ return project.contentAccess === false ? "/p/" + encodeURIComponent(slug) + "/" : null;
17
+ }
@@ -0,0 +1,21 @@
1
+ // This page never opens a project socket or loads project content modules.
2
+ var tabs = Array.from(document.querySelectorAll('[role="tab"]'));
3
+ function select(tab) {
4
+ tabs.forEach(function (item) {
5
+ var active = item === tab;
6
+ item.setAttribute("aria-selected", String(active));
7
+ item.tabIndex = active ? 0 : -1;
8
+ document.getElementById(item.getAttribute("aria-controls")).hidden = !active;
9
+ });
10
+ }
11
+ tabs.forEach(function (tab, index) {
12
+ tab.addEventListener("click", function () { select(tab); });
13
+ tab.addEventListener("keydown", function (event) {
14
+ if (["ArrowLeft", "ArrowRight", "Home", "End"].indexOf(event.key) === -1) return;
15
+ event.preventDefault();
16
+ var target = event.key === "Home" ? tabs[0] : event.key === "End" ? tabs[tabs.length - 1] : tabs[(index + (event.key === "ArrowRight" ? 1 : tabs.length - 1)) % tabs.length];
17
+ select(target);
18
+ target.focus();
19
+ });
20
+ });
21
+ select(tabs[0]);
@@ -8,6 +8,9 @@ import { switchProject } from './app-projects.js';
8
8
  import { closeTerminal } from './terminal.js';
9
9
  import { closeFileViewer } from './filebrowser.js';
10
10
  import { getActiveMentionMateIds, onMentionActiveChange, bindIconTooltip, hideIconTooltip, showMateCtxMenu } from './sidebar-mates.js';
11
+ import { chooseProjectActivationTarget } from './project-activation.js';
12
+ import { showProjectsHub } from './projects-hub.js';
13
+ import { openProjectSession } from './project-session-navigation.js';
11
14
 
12
15
  var FILTER_MODES = ['all', 'projects', 'mates'];
13
16
  var FILTER_LABELS = { all: 'All', projects: 'Projects', mates: 'Mates' };
@@ -98,9 +101,80 @@ function renderMateHeaderDefault(state) {
98
101
  renderMateProjectHeader(state, mate);
99
102
  }
100
103
 
104
+ function ordinaryProjects(state) {
105
+ return (state.projectsHubList || []).filter(function (project) {
106
+ return project && project.slug && project.isMate !== true;
107
+ });
108
+ }
109
+
110
+ function clayIsActive(state, clay) {
111
+ return !!clay && !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + clay.id;
112
+ }
113
+
114
+ export function clayReturnTargetForState(state) {
115
+ var target = state && state.clayReturnTarget;
116
+ var recordedSlug = target && target.projectSlug;
117
+ var candidates = recordedSlug ? [recordedSlug] : [];
118
+ var slug = chooseProjectActivationTarget(ordinaryProjects(state), candidates);
119
+ if (!slug) return null;
120
+ var sameProject = slug === recordedSlug;
121
+ return {
122
+ projectSlug: slug,
123
+ sessionId: sameProject && target && Number.isSafeInteger(Number(target.sessionId)) && Number(target.sessionId) > 0 ? Number(target.sessionId) : null,
124
+ };
125
+ }
126
+
127
+ function rememberClayReturnTarget(state) {
128
+ if (!state || state.homeShellVisible || state.dmMode || isMateWorkspace(state) || !state.currentSlug ||
129
+ state.activeProjectSlug !== state.currentSlug || state.sessionActivatedProjectSlug !== state.currentSlug) return;
130
+ var projects = ordinaryProjects(state);
131
+ var found = projects.some(function (project) { return project.slug === state.currentSlug; });
132
+ if (!found) return;
133
+ var target = { projectSlug: state.currentSlug, sessionId: Number.isSafeInteger(Number(state.activeSessionId)) && Number(state.activeSessionId) > 0 ? Number(state.activeSessionId) : null };
134
+ store.set({ clayReturnTarget: target });
135
+ return target;
136
+ }
137
+
138
+ function returnFromClay() {
139
+ var state = store.snap();
140
+ var target = clayReturnTargetForState(state);
141
+ if (!target) {
142
+ store.set({ pendingProjectSessionNavigation: null, clayReturnPendingProjectSlug: null, clayReturnTarget: null });
143
+ showProjectsHub();
144
+ return false;
145
+ }
146
+ store.set({ clayReturnTarget: null, clayReturnPendingProjectSlug: target.sessionId ? target.projectSlug : null });
147
+ if (target.sessionId) openProjectSession(target.projectSlug, target.sessionId);
148
+ else {
149
+ store.set({ pendingProjectSessionNavigation: null });
150
+ switchProject(target.projectSlug);
151
+ }
152
+ return true;
153
+ }
154
+
155
+ function updateClayBrand(state) {
156
+ var entry = document.querySelector('.icon-strip-brand');
157
+ if (!entry) return;
158
+ var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
159
+ var active = clayIsActive(state, clay);
160
+ var speaking = !!(state.youActivity && state.youActivity.active);
161
+ entry.dataset.clayMode = active ? 'mate' : 'project';
162
+ entry.classList.toggle('clay-speaking', speaking);
163
+ var avatar = entry.querySelector('.clay-brand-avatar');
164
+ var logo = entry.querySelector('.clay-brand-logo');
165
+ if (avatar && clay) avatar.src = mateAvatarUrl(clay, 38);
166
+ if (logo) logo.src = 'clay-studio-symbol.png';
167
+ entry.disabled = !clay;
168
+ entry.classList.toggle('active', active);
169
+ entry.setAttribute('aria-label', clay ? (active ? 'Return to project' : 'Open Clay') : 'Clay is unavailable');
170
+ if (active) entry.setAttribute('aria-current', 'page');
171
+ else entry.removeAttribute('aria-current');
172
+ }
173
+
101
174
  export function openMateWorkspace(mateId) {
102
175
  var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
103
176
  if (!mate) return;
177
+ if (mate.builtinKey === 'clay' && store.snap) rememberClayReturnTarget(store.snap());
104
178
  if (store.get('currentSlug') !== 'mate-' + mateId) {
105
179
  closeTerminal();
106
180
  closeFileViewer();
@@ -126,10 +200,14 @@ function createMateIcon(mateId) {
126
200
  avatarUrl: mate ? mateAvatarUrl(mate, 24) : '',
127
201
  };
128
202
  });
129
- button.addEventListener('contextmenu', function (event) {
130
- event.preventDefault();
203
+ function openContextMenu(event) {
204
+ event.preventDefault(); event.stopPropagation(); hideIconTooltip();
131
205
  var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
132
206
  if (mate) showMateCtxMenu(button, mate);
207
+ }
208
+ button.addEventListener('contextmenu', openContextMenu);
209
+ button.addEventListener('keydown', function (event) {
210
+ if (event.key === 'ContextMenu' || (event.shiftKey && event.key === 'F10')) openContextMenu(event);
133
211
  });
134
212
  return button;
135
213
  }
@@ -142,14 +220,7 @@ export function renderProjectMates() {
142
220
  var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
143
221
  var clayActive = !!clay && !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + clay.id;
144
222
  document.body.classList.toggle('clay-workspace-active', clayActive);
145
- var clayEntry = document.querySelector('.icon-strip-brand');
146
- if (clayEntry) {
147
- clayEntry.disabled = !clay;
148
- clayEntry.classList.toggle('active', clayActive);
149
- clayEntry.setAttribute('aria-label', clay ? 'Open Clay' : 'Clay is unavailable');
150
- if (clayActive) clayEntry.setAttribute('aria-current', 'page');
151
- else clayEntry.removeAttribute('aria-current');
152
- }
223
+ updateClayBrand(state);
153
224
  document.body.classList.toggle('mate-workspace-active', !state.homeShellVisible && isMateWorkspace(state));
154
225
  applyWorkspaceColor(mateForWorkspace(state));
155
226
  renderMateHeaderDefault(state);
@@ -216,19 +287,33 @@ export function initProjectMateNavigation() {
216
287
  var clayEntry = document.querySelector('.icon-strip-brand');
217
288
  if (clayEntry) {
218
289
  clayEntry.addEventListener('click', function () {
219
- var clay = navigationMates(store.get('cachedMatesList')).find(function (mate) { return mate.builtinKey === 'clay'; });
290
+ var state = store.snap();
291
+ var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
220
292
  hideIconTooltip();
221
- if (clay) openMateWorkspace(clay.id);
293
+ if (!clay) return;
294
+ if (!clayIsActive(state, clay)) {
295
+ openMateWorkspace(clay.id);
296
+ } else returnFromClay();
297
+ });
298
+ bindIconTooltip(clayEntry, function () {
299
+ var state = store.snap();
300
+ var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
301
+ return { text: clayIsActive(state, clay) ? 'Return to project' : 'Open Clay', kind: 'mate', avatarUrl: 'clay-studio-symbol.png' };
222
302
  });
223
- bindIconTooltip(clayEntry, { text: 'Clay', kind: 'mate', avatarUrl: 'clay-studio-symbol.png' });
224
303
  }
225
304
  var knob = document.getElementById('icon-strip-filter-knob');
226
305
  if (knob) knob.addEventListener('click', activateProjectMateFilter);
227
306
  store.subscribe(function (state, previous) {
307
+ if (state.clayReturnPendingProjectSlug && state.currentSlug !== previous.currentSlug &&
308
+ state.currentSlug !== state.clayReturnPendingProjectSlug) {
309
+ store.set({ pendingProjectSessionNavigation: null, clayReturnPendingProjectSlug: null });
310
+ } else if (state.clayReturnPendingProjectSlug && !state.pendingProjectSessionNavigation && previous.pendingProjectSessionNavigation) {
311
+ store.set({ clayReturnPendingProjectSlug: null });
312
+ }
228
313
  if (state.cachedMatesList !== previous.cachedMatesList || state.homeChatMateId !== previous.homeChatMateId ||
229
314
  state.homeShellVisible !== previous.homeShellVisible || state.projectsHubList !== previous.projectsHubList ||
230
315
  state.dmUnread !== previous.dmUnread || state.currentSlug !== previous.currentSlug ||
231
- state.dmMode !== previous.dmMode || state.connected !== previous.connected) renderProjectMates();
316
+ state.dmMode !== previous.dmMode || state.connected !== previous.connected || state.activeSessionId !== previous.activeSessionId || state.youActivity !== previous.youActivity) renderProjectMates();
232
317
  if (state.projectMateFilterMode !== previous.projectMateFilterMode ||
233
318
  state.projectMateFilterTurns !== previous.projectMateFilterTurns) applyProjectMateFilter();
234
319
  });
@@ -1,3 +1,4 @@
1
+ import { createHomeDebateQuestionCard, applyHomeDebateQuestion, resolveHomeDebateQuestion, markHomeDebateQuestionSubmitting } from './home-debate-planning.js';
1
2
  // Compact Clay conversation hosted inside the global search drawer.
2
3
  import { getWs } from './ws-ref.js';
3
4
  import { openProjectSession } from './project-session-navigation.js';
@@ -131,6 +132,7 @@ function appendActivityList(content, items, expanded, onToggle) {
131
132
  }
132
133
 
133
134
  function renderMessage(message, streaming) {
135
+ if (message.role === "question" && message.flow === "mcp_import") return createHomeDebateQuestionCard(message, respondToImport);
134
136
  var row;
135
137
  if (message.role === "user") {
136
138
  row = createUserBubble({ text: message.text || "" });
@@ -221,6 +223,13 @@ function stopSearchClayChat() {
221
223
  return true;
222
224
  }
223
225
 
226
+ function respondToImport(message, action, opener, answers) {
227
+ if (!state || message.status !== 'pending') return;
228
+ if (!send({ type: 'home_mcp_import_response', mateId: state.mateId, sessionId: state.sessionId, requestId: state.requestId, toolId: message.toolId, answers: answers || {} })) return;
229
+ markHomeDebateQuestionSubmitting(message, opener);
230
+ store.set({ searchClayChatState: state }); render();
231
+ }
232
+
224
233
  function respondToPermission(permission, decision) {
225
234
  if (!state || permission.status !== "pending") return false;
226
235
  if (!send({ type: "home_mate_permission_response", mateId: state.mateId, sessionId: state.sessionId, requestId: state.requestId, permissionRequestId: permission.permissionRequestId, decision: decision })) return false;
@@ -371,7 +380,12 @@ export function handleSearchClayMessage(msg) {
371
380
  state.updatedAt = Date.now();
372
381
  if (msg.mateId) state.mateId = msg.mateId;
373
382
  if (msg.type === "home_mate_session_identity" && msg.sessionId) state.sessionId = msg.sessionId;
374
- if (msg.type === "home_clay_activity") {
383
+ if (msg.type === 'home_mcp_import_question') {
384
+ if (state.stream) { state.messages.push({ role: 'assistant', text: state.stream }); state.stream = ''; }
385
+ state.messages = applyHomeDebateQuestion(state.messages, msg);
386
+ } else if (msg.type === 'home_mcp_import_question_resolved') {
387
+ state.messages = resolveHomeDebateQuestion(state.messages, msg);
388
+ } else if (msg.type === "home_clay_activity") {
375
389
  if (!state.awaitingCompletion) return true;
376
390
  state.activity = msg.phase || state.activity;
377
391
  state.step = typeof msg.step === "number" ? msg.step : state.step;
@@ -379,7 +393,7 @@ export function handleSearchClayMessage(msg) {
379
393
  } else if (msg.type === "home_mate_history") {
380
394
  state.sessionId = msg.sessionId || state.sessionId;
381
395
  state.localId = typeof msg.localId === "number" ? msg.localId : state.localId;
382
- state.messages = Array.isArray(msg.messages) ? msg.messages.filter(function (item) { return item && (item.role === "user" || item.role === "assistant"); }).map(function (item) { return { role: item.role, text: item.text || "" }; }) : state.messages;
396
+ state.messages = Array.isArray(msg.messages) ? msg.messages.filter(function (item) { return item && (item.role === "user" || item.role === "assistant" || item.role === "question" && item.flow === "mcp_import"); }).map(function (item) { return item.role === "question" ? Object.assign({}, item) : { role: item.role, text: item.text || "" }; }) : state.messages;
383
397
  state.processing = msg.isProcessing === true;
384
398
  state.awaitingCompletion = state.processing;
385
399
  state.pendingTurns = state.processing ? Math.max(1, state.pendingTurns) : 0;
@@ -9,7 +9,7 @@ export var FAVORITES_ID = "favorites";
9
9
  export var UNFILED_ID = "unfiled";
10
10
 
11
11
  export function defaultFolderState() {
12
- return { folders: [], assignments: {}, favorites: [], orders: {}, collapsed: {}, view: { sort: "activity", direction: "desc" } };
12
+ return { folders: [], assignments: {}, favorites: [], orders: {}, collapsed: {}, view: { sort: "activity", direction: "desc", showDateHeadings: true } };
13
13
  }
14
14
 
15
15
  export function sortUnits(units, view, order) {
@@ -78,6 +78,15 @@ export function paintViewMenu() {
78
78
  note.textContent = "Drag sessions to arrange them. Favorites keep their own order.";
79
79
  menu.appendChild(note);
80
80
  }
81
+ var dateToggle = document.createElement("button");
82
+ dateToggle.type = "button";
83
+ dateToggle.className = "session-folder-view-toggle";
84
+ dateToggle.dataset.focusKey = "showDateHeadings";
85
+ dateToggle.setAttribute("role", "switch");
86
+ dateToggle.setAttribute("aria-checked", String(view.showDateHeadings !== false));
87
+ dateToggle.innerHTML = '<span>Show date headings</span><span class="session-folder-view-toggle-track" aria-hidden="true"><span class="session-folder-view-toggle-thumb"></span></span>';
88
+ dateToggle.addEventListener("click", function () { sendViewPatch({ showDateHeadings: view.showDateHeadings === false }); });
89
+ menu.appendChild(dateToggle);
81
90
  if (focusKey) {
82
91
  var target = menu.querySelector('[data-focus-key="' + focusKey + '"]') || menu.querySelector(".session-folder-radio.selected");
83
92
  if (target) target.focus();