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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/bin/cli.js +9 -91
  4. package/lib/daemon-project-access.js +7 -1
  5. package/lib/daemon-settings.js +11 -0
  6. package/lib/daemon.js +10 -37
  7. package/lib/dicebear-avatar.js +62 -0
  8. package/lib/knowledge-db-service.js +175 -0
  9. package/lib/knowledge-db-validation.js +0 -0
  10. package/lib/knowledge-document-index.js +49 -0
  11. package/lib/knowledge-draft-store.js +56 -0
  12. package/lib/knowledge-file-store.js +199 -0
  13. package/lib/knowledge-markdown.js +128 -0
  14. package/lib/linear-store.js +35 -8
  15. package/lib/mate-knowledge-mcp-server.js +29 -6
  16. package/lib/mate-knowledge-service.js +23 -2
  17. package/lib/mate-knowledge-sync.js +36 -21
  18. package/lib/mate-workspace-color.js +4 -0
  19. package/lib/mates.js +18 -0
  20. package/lib/os-users.js +5 -16
  21. package/lib/project-connection.js +36 -9
  22. package/lib/project-debate-proposal.js +6 -0
  23. package/lib/project-debate-workspace.js +43 -0
  24. package/lib/project-debate.js +1 -0
  25. package/lib/project-filesystem.js +21 -39
  26. package/lib/project-knowledge.js +315 -136
  27. package/lib/project-mate-creation-proposal.js +1 -0
  28. package/lib/project-mate-interaction.js +2 -2
  29. package/lib/project-server-settings.js +31 -0
  30. package/lib/project-sessions.js +92 -182
  31. package/lib/project-user-knowledge.js +60 -0
  32. package/lib/project.js +59 -18
  33. package/lib/public/app.js +79 -71
  34. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  35. package/lib/public/css/clay-workspace.css +105 -0
  36. package/lib/public/css/debates-workbench.css +47 -0
  37. package/lib/public/css/home-debate-live.css +26 -0
  38. package/lib/public/css/home-hub.css +3 -1
  39. package/lib/public/css/home-mate-model-picker.css +15 -131
  40. package/lib/public/css/home-mate-settings.css +168 -16
  41. package/lib/public/css/icon-strip.css +249 -32
  42. package/lib/public/css/input.css +3 -1
  43. package/lib/public/css/mate-conversation.css +176 -0
  44. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  45. package/lib/public/css/mate-workspace-color.css +16 -0
  46. package/lib/public/css/mates.css +28 -28
  47. package/lib/public/css/messages.css +1 -114
  48. package/lib/public/css/mobile-nav.css +16 -16
  49. package/lib/public/css/overlays.css +8 -0
  50. package/lib/public/css/project-logs.css +7 -0
  51. package/lib/public/css/project-mate-creation.css +105 -0
  52. package/lib/public/css/project-settings-modal.css +56 -0
  53. package/lib/public/css/right-workbench.css +3 -3
  54. package/lib/public/css/server-settings-system.css +125 -0
  55. package/lib/public/css/session-folders.css +80 -21
  56. package/lib/public/css/session-github.css +25 -16
  57. package/lib/public/css/settings-ui.css +247 -22
  58. package/lib/public/css/shared-browser.css +2 -0
  59. package/lib/public/css/sidebar.css +183 -107
  60. package/lib/public/css/title-bar.css +156 -1
  61. package/lib/public/css/worker-delegation.css +47 -0
  62. package/lib/public/css/worker-proposal.css +72 -150
  63. package/lib/public/css/you.css +74 -0
  64. package/lib/public/index.html +203 -547
  65. package/lib/public/modules/account-island.js +232 -0
  66. package/lib/public/modules/admin-settings-email.js +197 -0
  67. package/lib/public/modules/admin-settings-invitations.js +183 -0
  68. package/lib/public/modules/admin-settings-projects.js +198 -0
  69. package/lib/public/modules/admin-settings-ui.js +100 -0
  70. package/lib/public/modules/admin-settings-users.js +331 -0
  71. package/lib/public/modules/admin.js +19 -949
  72. package/lib/public/modules/app-cursors.js +19 -43
  73. package/lib/public/modules/app-debate-ui.js +4 -3
  74. package/lib/public/modules/app-dm.js +0 -2
  75. package/lib/public/modules/app-header.js +106 -0
  76. package/lib/public/modules/app-message-router.js +12 -3
  77. package/lib/public/modules/app-messages.js +47 -61
  78. package/lib/public/modules/app-notifications.js +4 -7
  79. package/lib/public/modules/app-projects.js +5 -38
  80. package/lib/public/modules/app-rate-limit.js +70 -12
  81. package/lib/public/modules/app-rendering.js +12 -13
  82. package/lib/public/modules/avatar.js +25 -9
  83. package/lib/public/modules/chat-render-runtime.js +16 -4
  84. package/lib/public/modules/command-palette.js +2 -15
  85. package/lib/public/modules/context-sources.js +27 -18
  86. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  87. package/lib/public/modules/debate-controls.js +299 -0
  88. package/lib/public/modules/debate.js +2 -0
  89. package/lib/public/modules/debates-workbench.js +339 -0
  90. package/lib/public/modules/default-ai.js +88 -65
  91. package/lib/public/modules/header-tui-font.js +10 -20
  92. package/lib/public/modules/home-debate-controls.js +18 -289
  93. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  94. package/lib/public/modules/home-mate-color-picker.js +66 -0
  95. package/lib/public/modules/home-mate-creation.js +1 -1
  96. package/lib/public/modules/home-mate-instructions.js +90 -0
  97. package/lib/public/modules/home-mate-model-picker.js +80 -271
  98. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  99. package/lib/public/modules/home-mate-settings.js +118 -125
  100. package/lib/public/modules/home-shell.js +3 -0
  101. package/lib/public/modules/home-surface-boot.js +1 -9
  102. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  103. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  104. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  105. package/lib/public/modules/knowledge-history-state.js +55 -0
  106. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  107. package/lib/public/modules/knowledge-preview-links.js +70 -0
  108. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  109. package/lib/public/modules/linear-settings.js +3 -2
  110. package/lib/public/modules/markdown.js +5 -6
  111. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  112. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  113. package/lib/public/modules/mate-knowledge.js +11 -3
  114. package/lib/public/modules/mate-management.js +54 -10
  115. package/lib/public/modules/mate-sidebar.js +11 -1
  116. package/lib/public/modules/mate-workspace-color.js +33 -0
  117. package/lib/public/modules/mention.js +5 -13
  118. package/lib/public/modules/notification-settings.js +81 -0
  119. package/lib/public/modules/notifications.js +44 -28
  120. package/lib/public/modules/personal-settings-dialog.js +51 -0
  121. package/lib/public/modules/profile.js +66 -24
  122. package/lib/public/modules/project-chat-identity.js +79 -0
  123. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  124. package/lib/public/modules/project-debate-workspace.js +185 -0
  125. package/lib/public/modules/project-header-identity.js +127 -0
  126. package/lib/public/modules/project-logs-render.js +9 -4
  127. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  128. package/lib/public/modules/project-mate-navigation.js +238 -0
  129. package/lib/public/modules/project-session-navigation.js +29 -0
  130. package/lib/public/modules/project-settings-environment.js +106 -0
  131. package/lib/public/modules/project-settings.js +72 -628
  132. package/lib/public/modules/project-switcher.js +6 -8
  133. package/lib/public/modules/projects-hub-view.js +5 -6
  134. package/lib/public/modules/projects-hub.js +2 -6
  135. package/lib/public/modules/qrcode.js +27 -8
  136. package/lib/public/modules/search-clay-chat.js +8 -11
  137. package/lib/public/modules/server-settings-controls.js +316 -0
  138. package/lib/public/modules/server-settings-environment.js +194 -0
  139. package/lib/public/modules/server-settings-navigation.js +39 -0
  140. package/lib/public/modules/server-settings-state.js +54 -0
  141. package/lib/public/modules/server-settings.js +116 -788
  142. package/lib/public/modules/server-storage-settings.js +84 -0
  143. package/lib/public/modules/session-activity-tooltip.js +12 -0
  144. package/lib/public/modules/session-create-form.js +86 -14
  145. package/lib/public/modules/session-create.js +50 -7
  146. package/lib/public/modules/session-github.js +2 -1
  147. package/lib/public/modules/session-list-visibility.js +41 -0
  148. package/lib/public/modules/session-presence-ui.js +3 -0
  149. package/lib/public/modules/session-title-marquee.js +51 -0
  150. package/lib/public/modules/settings-env-values.js +34 -0
  151. package/lib/public/modules/settings-select.js +121 -0
  152. package/lib/public/modules/shared-browser.js +10 -7
  153. package/lib/public/modules/sidebar-mates.js +3 -50
  154. package/lib/public/modules/sidebar-mobile.js +63 -60
  155. package/lib/public/modules/sidebar-projects.js +84 -97
  156. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  157. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  158. package/lib/public/modules/sidebar-sessions.js +49 -50
  159. package/lib/public/modules/sidebar.js +11 -13
  160. package/lib/public/modules/stt-picker.js +14 -5
  161. package/lib/public/modules/terminal-prefs.js +22 -0
  162. package/lib/public/modules/theme.js +88 -52
  163. package/lib/public/modules/thinking-lifecycle.js +7 -6
  164. package/lib/public/modules/tool-palette-order.js +4 -3
  165. package/lib/public/modules/tool-palette.js +13 -3
  166. package/lib/public/modules/tools.js +22 -22
  167. package/lib/public/modules/user-settings-account.js +318 -0
  168. package/lib/public/modules/user-settings-appearance.js +55 -0
  169. package/lib/public/modules/user-settings-editors.js +24 -0
  170. package/lib/public/modules/user-settings-integrations.js +104 -0
  171. package/lib/public/modules/user-settings-layout.js +80 -0
  172. package/lib/public/modules/user-settings-preferences.js +48 -0
  173. package/lib/public/modules/user-settings-profile.js +53 -0
  174. package/lib/public/modules/user-settings-themes.js +38 -0
  175. package/lib/public/modules/user-settings-voice.js +75 -0
  176. package/lib/public/modules/user-settings.js +53 -480
  177. package/lib/public/modules/worker-proposal.js +42 -8
  178. package/lib/public/modules/worker-runtime-picker.js +115 -0
  179. package/lib/public/modules/you-activity.js +121 -0
  180. package/lib/public/modules/you.js +251 -0
  181. package/lib/public/style.css +20 -8
  182. package/lib/public/vendor/codemirror/LICENSE +21 -0
  183. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  184. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  185. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  186. package/lib/public/vendor/codemirror/markdown.js +886 -0
  187. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  188. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  189. package/lib/sdk-bridge.js +15 -3
  190. package/lib/sdk-message-processor.js +16 -4
  191. package/lib/sdk-quota-snapshot.js +37 -0
  192. package/lib/server-admin.js +11 -0
  193. package/lib/server-appearance-settings.js +71 -0
  194. package/lib/server-home-chat-events.js +1 -1
  195. package/lib/server-home-chat.js +25 -3
  196. package/lib/server-home-debate-planning.js +4 -1
  197. package/lib/server-home-debates.js +2 -0
  198. package/lib/server-home-mate-creation.js +4 -1
  199. package/lib/server-linear.js +14 -8
  200. package/lib/server-mate-instructions.js +44 -0
  201. package/lib/server-mates.js +12 -3
  202. package/lib/server-notification-settings.js +67 -0
  203. package/lib/server-palette.js +1 -1
  204. package/lib/server-settings.js +69 -1
  205. package/lib/server-storage.js +56 -0
  206. package/lib/server-user-knowledge.js +119 -0
  207. package/lib/server.js +31 -3
  208. package/lib/session-visibility.js +7 -2
  209. package/lib/sessions.js +34 -9
  210. package/lib/startup-projects.js +40 -0
  211. package/lib/storage-usage.js +127 -0
  212. package/lib/user-knowledge-service.js +167 -0
  213. package/lib/users-permissions.js +7 -0
  214. package/lib/users-preferences.js +0 -74
  215. package/lib/users-project-mate-filter-preferences.js +39 -0
  216. package/lib/users.js +6 -4
  217. package/lib/workspace-query-access.js +4 -1
  218. package/lib/ws-schema.js +52 -4
  219. package/lib/yoke/adapters/codex.js +8 -45
  220. package/lib/yoke/codex-model-catalog.js +40 -0
  221. package/lib/yoke/codex-rate-limits.js +26 -0
  222. package/package.json +12 -3
@@ -0,0 +1,121 @@
1
+ // Native selects own values and change events; this view adds desktop navigation.
2
+ export function enhanceSettingsSelects(root) {
3
+ if (!root) return;
4
+ root.querySelectorAll('select:not(.settings-nav-dropdown)').forEach(function (select) {
5
+ if (select.dataset.settingsPicker) return;
6
+ select.dataset.settingsPicker = 'true';
7
+ select.classList.add('settings-native-select');
8
+ var wrap = document.createElement('span');
9
+ wrap.className = 'settings-picker';
10
+ var button = document.createElement('button');
11
+ button.type = 'button';
12
+ button.className = 'settings-picker-trigger';
13
+ button.setAttribute('aria-haspopup', 'listbox');
14
+ button.setAttribute('aria-expanded', 'false');
15
+ var label = document.createElement('span');
16
+ label.className = 'settings-picker-value';
17
+ button.appendChild(label);
18
+ button.insertAdjacentHTML('beforeend', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>');
19
+ var menu = document.createElement('span');
20
+ menu.className = 'settings-picker-menu';
21
+ menu.id = (select.id || 'settings-select-' + document.querySelectorAll('.settings-picker').length) + '-menu';
22
+ menu.setAttribute('role', 'listbox');
23
+ menu.hidden = true;
24
+ button.setAttribute('aria-controls', menu.id);
25
+ var enclosingLabel = select.closest('label');
26
+ var associatedLabel = select.labels && select.labels[0];
27
+ var name = select.getAttribute('aria-label') || (enclosingLabel && enclosingLabel.querySelector('.settings-label') || {}).textContent || (associatedLabel && associatedLabel.textContent) || 'Choose a value';
28
+ name = name.trim();
29
+ menu.setAttribute('aria-label', name);
30
+ select.insertAdjacentElement('afterend', wrap);
31
+ wrap.append(button, menu);
32
+ var search = '';
33
+ var searchAt = 0;
34
+ function outside(event) { if (!wrap.contains(event.target)) close(false); }
35
+ function close(focus) {
36
+ document.removeEventListener('pointerdown', outside);
37
+ menu.hidden = true;
38
+ button.setAttribute('aria-expanded', 'false');
39
+ if (focus) button.focus();
40
+ }
41
+ function sync() {
42
+ label.textContent = (select.selectedOptions[0] || {}).textContent || 'Choose…';
43
+ button.setAttribute('aria-label', name + ': ' + label.textContent);
44
+ button.disabled = select.disabled;
45
+ if (button.disabled) close(false);
46
+ menu.replaceChildren();
47
+ Array.from(select.options).forEach(function (option) {
48
+ var item = document.createElement('button');
49
+ item.type = 'button';
50
+ item.tabIndex = -1;
51
+ item.setAttribute('role', 'option');
52
+ item.textContent = option.textContent;
53
+ item.setAttribute('aria-selected', String(option.selected));
54
+ item.disabled = option.disabled || (option.parentElement.tagName === 'OPTGROUP' && option.parentElement.disabled);
55
+ item.hidden = option.hidden;
56
+ item.onclick = function (event) {
57
+ event.preventDefault();
58
+ select.value = option.value;
59
+ select.dispatchEvent(new Event('change', { bubbles: true }));
60
+ sync();
61
+ close(true);
62
+ };
63
+ menu.appendChild(item);
64
+ });
65
+ }
66
+ function items() { return Array.from(menu.querySelectorAll('button:not(:disabled):not([hidden])')); }
67
+ function open() {
68
+ sync();
69
+ var choices = items();
70
+ if (!choices.length || button.disabled) return;
71
+ menu.hidden = false;
72
+ button.setAttribute('aria-expanded', 'true');
73
+ document.addEventListener('pointerdown', outside);
74
+ var bounds = button.getBoundingClientRect();
75
+ var boundary = wrap.closest('.server-settings-content, .us-modal-content, .us-content, .ps-content, .admin-modal');
76
+ var bottom = Math.min(window.innerHeight - 8, boundary ? boundary.getBoundingClientRect().bottom - 8 : window.innerHeight - 8);
77
+ var above = bounds.top - Math.max(8, boundary ? boundary.getBoundingClientRect().top + 8 : 8);
78
+ var below = bottom - bounds.bottom - 4;
79
+ var flip = below < 180 && above > below;
80
+ menu.classList.toggle('settings-picker-menu-above', flip);
81
+ menu.style.maxHeight = Math.max(64, Math.min(240, flip ? above : below)) + 'px';
82
+ var selected = menu.querySelector('[aria-selected="true"]:not(:disabled):not([hidden])');
83
+ (selected || choices[0]).focus();
84
+ }
85
+ button.onclick = function (event) {
86
+ event.preventDefault();
87
+ if (menu.hidden) open(); else close(false);
88
+ };
89
+ wrap.addEventListener('keydown', function (event) {
90
+ if (event.key === 'Escape' && !menu.hidden) {
91
+ event.preventDefault(); event.stopPropagation(); close(true); return;
92
+ }
93
+ // Return to the trigger before native Tab traversal leaves this control.
94
+ if (event.key === 'Tab' && !menu.hidden) { close(true); return; }
95
+ var navigation = ['ArrowDown', 'ArrowUp', 'Home', 'End'].indexOf(event.key) >= 0;
96
+ var typing = event.key.length === 1 && event.key !== ' ' && !event.ctrlKey && !event.metaKey && !event.altKey;
97
+ if (!navigation && !typing) return;
98
+ event.preventDefault();
99
+ var wasClosed = menu.hidden;
100
+ if (wasClosed) open();
101
+ var choices = items();
102
+ if (!choices.length) return;
103
+ var index = choices.indexOf(document.activeElement);
104
+ if (typing) {
105
+ search = Date.now() - searchAt > 700 ? event.key : search + event.key;
106
+ searchAt = Date.now();
107
+ var match = choices.find(function (item) { return item.textContent.trim().toLowerCase().indexOf(search.toLowerCase()) === 0; });
108
+ if (match) match.focus();
109
+ } else if (!wasClosed || event.key === 'Home' || event.key === 'End') {
110
+ index = event.key === 'Home' ? 0 : event.key === 'End' ? choices.length - 1 : (index + (event.key === 'ArrowDown' ? 1 : -1) + choices.length) % choices.length;
111
+ choices[index].focus();
112
+ }
113
+ });
114
+ wrap.addEventListener('focusout', function (event) { if (!wrap.contains(event.relatedTarget)) close(false); });
115
+ select.addEventListener('change', sync);
116
+ select.addEventListener('settings-sync', sync);
117
+ // Async option loading and pending/failed saves retain native control behavior.
118
+ new MutationObserver(sync).observe(select, { attributes: true, attributeFilter: ['disabled'], childList: true, subtree: true, characterData: true });
119
+ sync();
120
+ });
121
+ }
@@ -8,10 +8,11 @@ import { activityHtml, renderBrowserActivity } from './shared-browser-activity.j
8
8
  import { viewportMenuHtml, bindViewportMenu, renderViewportMenu, closeViewportMenu } from './shared-browser-viewport-menu.js';
9
9
 
10
10
  function desktop() { return window.matchMedia('(min-width: 1024px)').matches; }
11
- function eligible() {
11
+ function visibleEntry() {
12
12
  var permissions = store.get('permissions');
13
- return desktop() && !!store.get('currentSlug') && !!store.get('activeSessionId') && !store.get('dmMode') && (!permissions || permissions.terminal !== false);
13
+ return desktop() && !!store.get('currentSlug') && !store.get('dmMode') && (!permissions || permissions.terminal !== false);
14
14
  }
15
+ function eligible() { return visibleEntry() && !!store.get('activeSessionId'); }
15
16
  function panel() { return document.getElementById('shared-browser-panel'); }
16
17
  function ui(patch) { store.set({ sharedBrowserUi: Object.assign({}, store.get('sharedBrowserUi'), patch) }); }
17
18
  function send(type, fields) {
@@ -78,12 +79,14 @@ function ensurePanel() {
78
79
  function renderEntry() {
79
80
  var button = document.getElementById('shared-browser-btn');
80
81
  if (!button) return;
81
- button.classList.toggle('hidden', !eligible());
82
+ button.classList.toggle('hidden', !visibleEntry());
82
83
  var browser = store.get('sharedBrowser');
83
- button.title = browser ? 'View browser · ' + (store.get('connected') ? browser.phase : 'Disconnected') : 'Open browser';
84
- button.setAttribute('aria-label', browser ? 'View browser' : 'Open browser');
84
+ var ready = !!store.get('connected') && !!store.get('activeSessionId');
85
+ var label = !store.get('connected') ? 'Browser · Connecting…' : !store.get('activeSessionId') ? 'Browser · Loading session…' : browser ? 'View browser' : 'Open browser';
86
+ button.title = ready && browser ? label + ' · ' + browser.phase : label;
87
+ button.setAttribute('aria-label', label);
85
88
  button.setAttribute('aria-expanded', String(!!store.get('sharedBrowserOpen')));
86
- button.disabled = !store.get('connected');
89
+ button.disabled = !ready;
87
90
  }
88
91
  function controlText(browser, live, connected) {
89
92
  if (!connected) return 'Disconnected. Status returns on reconnect.';
@@ -158,7 +161,7 @@ function selectBrowserTab(id) {
158
161
  send('view', { visible: desktop() && !document.hidden }); render();
159
162
  }
160
163
  export function openSharedBrowser() {
161
- if (!eligible()) return;
164
+ if (!eligible() || !store.get('connected')) return;
162
165
  ensurePanel();
163
166
  claimRightWorkbench('browser');
164
167
  store.set({ sharedBrowserOpen: true });
@@ -10,6 +10,9 @@ import { closeProjectCtxMenu } from './sidebar-projects.js';
10
10
  import { spawnDustParticles } from './sidebar.js';
11
11
  import { openDm } from './app-dm.js';
12
12
  import { showMateManagementMenu, closeMateManagementMenu } from './mate-management.js';
13
+ import { showIconTooltip, hideIconTooltip } from './icon-strip-tooltip.js';
14
+
15
+ export { showIconTooltip, showIconTooltipHtml, hideIconTooltip, bindIconTooltip } from './icon-strip-tooltip.js';
13
16
 
14
17
  function sendWs(msg) {
15
18
  var ws = getWs();
@@ -59,9 +62,6 @@ function notifyMentionListeners() {
59
62
  }
60
63
  var _lastUserStripJson = "";
61
64
 
62
- // --- Icon strip tooltip ---
63
- var iconStripTooltip = null;
64
-
65
65
  // --- DM user context menu ---
66
66
  var userCtxMenu = null;
67
67
 
@@ -81,53 +81,6 @@ export function initSidebarMates() {
81
81
  });
82
82
  }
83
83
 
84
- export function showIconTooltip(el, text) {
85
- hideIconTooltip();
86
- var tip = document.createElement("div");
87
- tip.className = "icon-strip-tooltip";
88
- tip.textContent = text;
89
- document.body.appendChild(tip);
90
- iconStripTooltip = tip;
91
-
92
- requestAnimationFrame(function () {
93
- var rect = el.getBoundingClientRect();
94
- tip.style.top = (rect.top + rect.height / 2 - tip.offsetHeight / 2) + "px";
95
- tip.classList.add("visible");
96
- });
97
- }
98
-
99
- export function showIconTooltipHtml(el, html, options) {
100
- hideIconTooltip();
101
- options = options || {};
102
- var tip = document.createElement("div");
103
- tip.className = "icon-strip-tooltip" + (options.className ? " " + options.className : "");
104
- tip.style.whiteSpace = "normal";
105
- tip.style.maxWidth = "260px";
106
- tip.innerHTML = html;
107
- document.body.appendChild(tip);
108
- iconStripTooltip = tip;
109
-
110
- requestAnimationFrame(function () {
111
- var rect = el.getBoundingClientRect();
112
- if (options.placement === "bottom") {
113
- var left = rect.left + rect.width / 2 - tip.offsetWidth / 2;
114
- left = Math.max(12, Math.min(left, window.innerWidth - tip.offsetWidth - 12));
115
- tip.style.left = left + "px";
116
- tip.style.top = (rect.bottom + 8) + "px";
117
- } else {
118
- tip.style.top = (rect.top + rect.height / 2 - tip.offsetHeight / 2) + "px";
119
- }
120
- tip.classList.add("visible");
121
- });
122
- }
123
-
124
- export function hideIconTooltip() {
125
- if (iconStripTooltip) {
126
- iconStripTooltip.remove();
127
- iconStripTooltip = null;
128
- }
129
- }
130
-
131
84
  export function closeUserCtxMenu() {
132
85
  if (userCtxMenu) {
133
86
  userCtxMenu.remove();
@@ -1,3 +1,4 @@
1
+ import { openMateWorkspace, isMateWorkspace } from './project-mate-navigation.js';
1
2
  import { appendGithubWork } from './session-github.js';
2
3
  // sidebar-mobile.js - Mobile sheet overlays, tab bar, and mobile-specific rendering
3
4
  // Extracted from sidebar.js (PR-38)
@@ -10,7 +11,6 @@ import { getCurrentTheme, getChatLayout, setChatLayout } from './theme.js';
10
11
  import { openCommandPalette } from './command-palette.js';
11
12
  import { openProjectSettings } from './project-settings.js';
12
13
  import { getCachedSessions, openSessionContextMenu } from './sidebar-sessions.js';
13
- import { VENDOR_AVATARS } from './app-rendering.js';
14
14
  import { renderSessionPresence } from './session-presence-ui.js';
15
15
  import {
16
16
  canOrganizeSession, computeLayout, createStarButton, renderFavoritesSection, renderOrdinarySections
@@ -26,9 +26,7 @@ import {
26
26
  import {
27
27
  getCurrentDmUserId,
28
28
  getCachedMates,
29
- getCachedDmFavorites,
30
- getCachedDmUnread,
31
- getCachedDmRemovedUsers
29
+ getCachedDmUnread
32
30
  } from './sidebar-mates.js';
33
31
 
34
32
  import { store } from './store.js';
@@ -36,8 +34,6 @@ import { getWs } from './ws-ref.js';
36
34
  import { dismissOverlayPanels, closeSidebar } from './sidebar.js';
37
35
  import { openWorkerWithDriver } from './split-view.js';
38
36
  import { switchProject, getCachedProjects } from './app-projects.js';
39
- import { showHomeHub } from './app-home-hub.js';
40
- import { openHomeChat } from './home-mate-chat.js';
41
37
  import { openTerminal } from './terminal.js';
42
38
  import { requestKnowledgeList } from './mate-knowledge.js';
43
39
  import { loadRootDirectory, reopenFileViewer } from './filebrowser.js';
@@ -46,6 +42,9 @@ import {
46
42
  renderMobileDriverHierarchy,
47
43
  renderMobileOrphanHierarchy
48
44
  } from './sidebar-session-hierarchy.js';
45
+ import { ordinarySessionListSessions } from './session-list-visibility.js';
46
+ import { formatSessionActivity } from './session-activity-tooltip.js';
47
+ import { bindSessionTitleMarquee } from './session-title-marquee.js';
49
48
  import { summarizeSidebarItems } from './sidebar-session-counts.js';
50
49
  import { cancelSessionInlineRename, closeSessionContextMenu } from './session-context-menu.js';
51
50
 
@@ -258,13 +257,10 @@ function renderSheetSessions(listEl) {
258
257
  })(getCachedProjectList()[ci]);
259
258
  }
260
259
 
261
- var favoriteChipMates = getCachedMates().filter(function (m) {
262
- if (getCachedDmRemovedUsers()[m.id]) return false;
263
- if (getCachedDmFavorites().indexOf(m.id) !== -1) return true;
264
- if (getCachedDmUnread()[m.id] && getCachedDmUnread()[m.id] > 0) return true;
265
- return false;
260
+ var visibleChipMates = getCachedMates().filter(function (m) {
261
+ return m && !m.archived;
266
262
  });
267
- var sortedChipMates = favoriteChipMates.sort(function (a, b) {
263
+ var sortedChipMates = visibleChipMates.sort(function (a, b) {
268
264
  var aBuiltin = a.builtinKey ? 1 : 0;
269
265
  var bBuiltin = b.builtinKey ? 1 : 0;
270
266
  if (aBuiltin !== bBuiltin) return bBuiltin - aBuiltin;
@@ -275,7 +271,7 @@ function renderSheetSessions(listEl) {
275
271
  var mp = mate.profile || {};
276
272
  var chip = document.createElement("button");
277
273
  chip.className = "mobile-chat-chip";
278
- if (getCurrentDmUserId() === mate.id) chip.classList.add("active");
274
+ if (getCurrentDmUserId() === mate.id || getCachedCurrentSlug() === "mate-" + mate.id) chip.classList.add("active");
279
275
  chip.dataset.type = "mate";
280
276
  chip.dataset.mateId = mate.id;
281
277
 
@@ -331,8 +327,7 @@ function renderSheetSessions(listEl) {
331
327
  renderMobileSessionsInto(sessionListEl);
332
328
  } else if (type === "mate") {
333
329
  closeMobileSheet();
334
- showHomeHub();
335
- openHomeChat(mateId);
330
+ openMateWorkspace(mateId);
336
331
  }
337
332
 
338
333
  refreshIcons();
@@ -388,24 +383,28 @@ function renderSheetSessions(listEl) {
388
383
  // Helper: create a mobile session item element
389
384
  function createMobileSessionItem(s, options) {
390
385
  var itemOptions = options || {};
386
+ var displaySession = s;
387
+ if (itemOptions.promotedActive || itemOptions.promotedProcessing || itemOptions.promotedUnread !== undefined) {
388
+ displaySession = Object.assign({}, s, {
389
+ active: !!(s.active || itemOptions.promotedActive),
390
+ isProcessing: !!(s.isProcessing || itemOptions.promotedProcessing),
391
+ unread: (s.unread || 0) + (itemOptions.promotedUnread || 0),
392
+ });
393
+ }
391
394
  var el = document.createElement("div");
392
- el.className = "mobile-session-item" + (s.active ? " active" : "");
395
+ el.className = "mobile-session-item" + (displaySession.active ? " active" : "");
393
396
  el.dataset.sessionId = String(s.id);
394
397
  if (itemOptions.worker) el.classList.add("mobile-worker-item");
395
398
 
396
- // Vendor badge (legacy sessions without a vendor are Claude)
397
- var mVendor = s.vendor || "claude";
398
- var vendorIcon = document.createElement("img");
399
- vendorIcon.className = "mobile-session-vendor-icon";
400
- vendorIcon.src = VENDOR_AVATARS[mVendor] || VENDOR_AVATARS.claude;
401
- vendorIcon.alt = "";
402
- el.appendChild(vendorIcon);
399
+ var titleLine = document.createElement("span");
400
+ titleLine.className = "mobile-session-title-line";
401
+ el.appendChild(titleLine);
403
402
 
404
403
  // Processing dot (left side, before title)
405
- if (s.isProcessing) {
404
+ if (displaySession.isProcessing) {
406
405
  var dot = document.createElement("span");
407
406
  dot.className = "mobile-session-processing";
408
- el.appendChild(dot);
407
+ titleLine.appendChild(dot);
409
408
  }
410
409
 
411
410
  var titleSpan = document.createElement("span");
@@ -415,9 +414,12 @@ function createMobileSessionItem(s, options) {
415
414
  if (event.key === "Enter" || event.key === " ") { event.preventDefault(); el.click(); }
416
415
  });
417
416
  titleSpan.className = "mobile-session-title";
417
+ titleSpan.setAttribute("aria-label", s.title || "New Session");
418
+ var activityTitle = formatSessionActivity(s.lastActivity);
419
+ if (activityTitle) titleSpan.title = activityTitle;
418
420
  titleSpan.appendChild(document.createTextNode(s.title || "New Session"));
419
- el.appendChild(titleSpan);
420
- appendGithubWork(el, s, titleSpan);
421
+ titleLine.appendChild(titleSpan);
422
+ appendGithubWork(el, s, titleLine);
421
423
  renderSessionPresence(el, String(s.id));
422
424
 
423
425
  if (itemOptions.worker) {
@@ -428,16 +430,18 @@ function createMobileSessionItem(s, options) {
428
430
  }
429
431
 
430
432
  // Unread badge (right side)
431
- if (s.unread > 0 && !s.active) {
433
+ if (displaySession.unread > 0 && !displaySession.active) {
432
434
  var badge = document.createElement("span");
433
435
  badge.className = "mobile-session-unread";
434
- badge.textContent = s.unread > 99 ? "99+" : String(s.unread);
436
+ badge.textContent = displaySession.unread > 99 ? "99+" : String(displaySession.unread);
435
437
  el.appendChild(badge);
436
438
  }
437
439
 
438
- if (!itemOptions.worker && canOrganizeSession(s)) {
439
- el.appendChild(createStarButton(s, "mobile-session-star"));
440
- }
440
+ var mobileActions = document.createElement("span");
441
+ mobileActions.className = "mobile-session-actions";
442
+ if (!itemOptions.worker && canOrganizeSession(s)) mobileActions.appendChild(createStarButton(s, "mobile-session-star"));
443
+ el.appendChild(mobileActions);
444
+ bindSessionTitleMarquee(el, titleSpan, mobileActions);
441
445
 
442
446
  el.addEventListener("contextmenu", function (event) {
443
447
  event.preventDefault();
@@ -460,6 +464,7 @@ function createMobileSessionItem(s, options) {
460
464
  return el;
461
465
  }
462
466
 
467
+
463
468
  // Helper: create a mobile loop child element (individual session inside a group)
464
469
  function createMobileLoopChild(s) {
465
470
  var el = document.createElement("button");
@@ -698,7 +703,7 @@ function createMobileLoopGroup(loopId, children, groupKey) {
698
703
  // Helper: render sorted sessions into a container with date groups (with loop session grouping)
699
704
  export function renderMobileSessionsInto(container) {
700
705
  // Partition: loop sessions vs normal sessions (same logic as desktop renderSessionList)
701
- var sessions = getCachedSessions();
706
+ var sessions = ordinarySessionListSessions(getCachedSessions());
702
707
  var hierarchy = prepareSidebarHierarchy(sessions);
703
708
  var hierarchyByDriver = hierarchy.byDriver;
704
709
  var loopGroups = {};
@@ -706,7 +711,7 @@ export function renderMobileSessionsInto(container) {
706
711
  for (var i = 0; i < sessions.length; i++) {
707
712
  var s = sessions[i];
708
713
  if (s.sessionRole === "worker") continue;
709
- if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph" && s.loop.source !== "debate") {
714
+ if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph") {
710
715
  continue;
711
716
  } else if (s.loop && s.loop.loopId) {
712
717
  var startedAt = s.loop.startedAt || 0;
@@ -726,11 +731,6 @@ export function renderMobileSessionsInto(container) {
726
731
  if (driverRoot) items.push({ type: "driver-hierarchy", data: driverRoot.driver, root: driverRoot, lastActivity: driverRoot.lastActivity });
727
732
  else items.push({ type: "session", data: normalSessions[j], lastActivity: normalSessions[j].lastActivity || 0 });
728
733
  }
729
- if (hierarchy.orphans.length) {
730
- var orphanActivity = 0;
731
- for (var oi = 0; oi < hierarchy.orphans.length; oi++) orphanActivity = Math.max(orphanActivity, hierarchy.orphans[oi].lastActivity || 0);
732
- items.push({ type: "orphan-workers", workers: hierarchy.orphans, lastActivity: orphanActivity });
733
- }
734
734
  var groupKeys = Object.keys(loopGroups);
735
735
  for (var k = 0; k < groupKeys.length; k++) {
736
736
  var gk = groupKeys[k];
@@ -753,8 +753,8 @@ export function renderMobileSessionsInto(container) {
753
753
  sessions: getCachedSessions(),
754
754
  renderItem: function (it) {
755
755
  if (it.type === "loop") return createMobileLoopGroup(it.loopId, it.children, it.groupKey);
756
- if (it.type === "driver-hierarchy") return renderMobileDriverHierarchy(it.root, createMobileSessionItem, refreshMobileChatSheet, matchIds);
757
- if (it.type === "orphan-workers") return renderMobileOrphanHierarchy(it.workers, createMobileSessionItem, refreshMobileChatSheet, matchIds);
756
+ if (it.type === "driver-hierarchy") return renderMobileDriverHierarchy(it.root, createMobileSessionItem, refreshMobileChatSheet);
757
+ if (it.type === "orphan-workers") return document.createElement("div");
758
758
  return createMobileSessionItem(it.data);
759
759
  },
760
760
  };
@@ -787,7 +787,7 @@ export function refreshMobileChatSheet() {
787
787
  var isDmActive = !!getCurrentDmUserId();
788
788
  if (chip.dataset.type === "project" && chip.dataset.slug === getCachedCurrentSlug() && !isDmActive) {
789
789
  chip.classList.add("active");
790
- } else if (chip.dataset.type === "mate" && chip.dataset.mateId === getCurrentDmUserId()) {
790
+ } else if (chip.dataset.type === "mate" && (chip.dataset.mateId === getCurrentDmUserId() || getCachedCurrentSlug() === "mate-" + chip.dataset.mateId)) {
791
791
  chip.classList.add("active");
792
792
  }
793
793
 
@@ -959,14 +959,17 @@ function renderSearchResults(container, query) {
959
959
  }
960
960
  }
961
961
 
962
- function renderSheetTools(listEl) {
962
+ export function renderSheetTools(listEl) {
963
963
  var isMateDm = document.body.classList.contains("mate-dm-active");
964
+ var isMateWorkspaceActive = isMateWorkspace(store.snap());
965
+ var isMateDmSurface = isMateDm && !isMateWorkspaceActive;
964
966
 
965
- var items = isMateDm ? [
967
+ var items = isMateDmSurface ? [
966
968
  { icon: "brain", label: "Memory", action: "mate-memory" },
967
969
  { icon: "book-open", label: "Knowledge", action: "mate-knowledge" },
968
970
  { icon: "sticky-note", label: "Sticky Notes", action: "mate-sticky" },
969
- { icon: "puzzle", label: "MCP / Skills", action: "mate-skills" }
971
+ { icon: "puzzle", label: "MCP / Skills", action: "mate-skills" },
972
+ { icon: "messages-square", label: "Debates", action: "debates" }
970
973
  ] : [
971
974
  { icon: "folder-tree", label: "Files", action: "files" },
972
975
  { icon: "square-terminal", label: "Terminal", action: "terminal" },
@@ -974,6 +977,15 @@ function renderSheetTools(listEl) {
974
977
  { icon: "notebook-tabs", label: "Logs", action: "project-logs" }
975
978
  ];
976
979
 
980
+ if (isMateWorkspaceActive) {
981
+ items = items.filter(function (item) { return item.action !== "files" && item.action !== "terminal" && item.action !== "issues" && item.action !== "project-logs"; });
982
+ items.unshift({ icon: "book-open", label: "Knowledge", action: "knowledge-workbench" });
983
+ items.push({ icon: "messages-square", label: "Debates", action: "debates" });
984
+ }
985
+
986
+ if (isMateWorkspaceActive && document.body.classList.contains('clay-workspace-active')) {
987
+ items.unshift({ icon: 'user-round', label: 'Me', action: 'me' });
988
+ }
977
989
  for (var i = 0; i < items.length; i++) {
978
990
  (function (item) {
979
991
  var btn = document.createElement("button");
@@ -990,6 +1002,12 @@ function renderSheetTools(listEl) {
990
1002
  targetId = "issues-btn";
991
1003
  } else if (item.action === "project-logs") {
992
1004
  targetId = "project-logs-btn";
1005
+ } else if (item.action === "me") {
1006
+ targetId = "you-button";
1007
+ } else if (item.action === "knowledge-workbench") {
1008
+ targetId = "knowledge-workbench-btn";
1009
+ } else if (item.action === "debates") {
1010
+ targetId = isMateWorkspaceActive ? "mate-project-debates-btn" : (isMateDmSurface ? "mate-debates-btn" : null);
993
1011
  } else if (item.action === "mate-knowledge") {
994
1012
  setTimeout(function () { openMobileSheet("mate-knowledge"); }, 250);
995
1013
  return;
@@ -1207,7 +1225,6 @@ export function initSidebarMobile() {
1207
1225
  // --- Mobile tab bar ---
1208
1226
  var mobileTabBar = document.getElementById("mobile-tab-bar");
1209
1227
  var mobileTabs = mobileTabBar ? mobileTabBar.querySelectorAll(".mobile-tab") : [];
1210
- var mobileHomeBtn = document.getElementById("mobile-home-btn");
1211
1228
 
1212
1229
  function setMobileTabActive(tabName) {
1213
1230
  for (var i = 0; i < mobileTabs.length; i++) {
@@ -1217,13 +1234,6 @@ export function initSidebarMobile() {
1217
1234
  mobileTabs[i].classList.remove("active");
1218
1235
  }
1219
1236
  }
1220
- if (mobileHomeBtn) {
1221
- if (tabName === "home") {
1222
- mobileHomeBtn.classList.add("active");
1223
- } else {
1224
- mobileHomeBtn.classList.remove("active");
1225
- }
1226
- }
1227
1237
  }
1228
1238
 
1229
1239
  for (var t = 0; t < mobileTabs.length; t++) {
@@ -1248,11 +1258,4 @@ export function initSidebarMobile() {
1248
1258
  })(mobileTabs[t]);
1249
1259
  }
1250
1260
 
1251
- if (mobileHomeBtn) {
1252
- mobileHomeBtn.addEventListener("click", function () {
1253
- closeSidebar();
1254
- setMobileTabActive("home");
1255
- showHomeHub();
1256
- });
1257
- }
1258
1261
  }