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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/bin/cli.js +9 -91
  4. package/lib/builtin-mates.js +7 -7
  5. package/lib/daemon-project-access.js +7 -1
  6. package/lib/daemon-settings.js +11 -0
  7. package/lib/daemon.js +10 -37
  8. package/lib/dicebear-avatar.js +62 -0
  9. package/lib/knowledge-db-service.js +175 -0
  10. package/lib/knowledge-db-validation.js +0 -0
  11. package/lib/knowledge-document-index.js +49 -0
  12. package/lib/knowledge-draft-store.js +56 -0
  13. package/lib/knowledge-file-store.js +199 -0
  14. package/lib/knowledge-markdown.js +128 -0
  15. package/lib/linear-store.js +35 -8
  16. package/lib/mate-identity-file.js +39 -0
  17. package/lib/mate-knowledge-mcp-server.js +29 -6
  18. package/lib/mate-knowledge-service.js +23 -2
  19. package/lib/mate-knowledge-sync.js +37 -22
  20. package/lib/mate-ready-creation.js +2 -1
  21. package/lib/mate-workspace-color.js +4 -0
  22. package/lib/mates-identity.js +4 -4
  23. package/lib/mates-prompts.js +4 -3
  24. package/lib/mates.js +30 -11
  25. package/lib/os-users.js +5 -16
  26. package/lib/project-connection.js +36 -9
  27. package/lib/project-debate-proposal.js +6 -0
  28. package/lib/project-debate-workspace.js +43 -0
  29. package/lib/project-debate.js +4 -2
  30. package/lib/project-filesystem.js +21 -39
  31. package/lib/project-knowledge.js +315 -136
  32. package/lib/project-mate-creation-proposal.js +1 -0
  33. package/lib/project-mate-interaction.js +7 -6
  34. package/lib/project-server-settings.js +31 -0
  35. package/lib/project-sessions.js +92 -182
  36. package/lib/project-user-knowledge.js +60 -0
  37. package/lib/project.js +62 -21
  38. package/lib/public/app.js +79 -71
  39. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  40. package/lib/public/css/clay-workspace.css +105 -0
  41. package/lib/public/css/debates-workbench.css +47 -0
  42. package/lib/public/css/home-debate-live.css +26 -0
  43. package/lib/public/css/home-hub.css +3 -1
  44. package/lib/public/css/home-mate-model-picker.css +15 -131
  45. package/lib/public/css/home-mate-settings.css +168 -16
  46. package/lib/public/css/icon-strip.css +249 -32
  47. package/lib/public/css/input.css +3 -1
  48. package/lib/public/css/mate-conversation.css +176 -0
  49. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  50. package/lib/public/css/mate-workspace-color.css +16 -0
  51. package/lib/public/css/mates.css +28 -28
  52. package/lib/public/css/menus.css +3 -0
  53. package/lib/public/css/messages.css +1 -114
  54. package/lib/public/css/mobile-nav.css +16 -16
  55. package/lib/public/css/overlays.css +15 -0
  56. package/lib/public/css/project-logs.css +7 -0
  57. package/lib/public/css/project-mate-creation.css +105 -0
  58. package/lib/public/css/project-settings-modal.css +56 -0
  59. package/lib/public/css/right-workbench.css +3 -3
  60. package/lib/public/css/server-settings-system.css +125 -0
  61. package/lib/public/css/session-folders.css +80 -21
  62. package/lib/public/css/session-github.css +25 -16
  63. package/lib/public/css/settings-ui.css +247 -22
  64. package/lib/public/css/shared-browser.css +7 -3
  65. package/lib/public/css/sidebar.css +183 -107
  66. package/lib/public/css/title-bar.css +156 -1
  67. package/lib/public/css/worker-delegation.css +47 -0
  68. package/lib/public/css/worker-proposal.css +72 -150
  69. package/lib/public/css/you.css +74 -0
  70. package/lib/public/index.html +206 -548
  71. package/lib/public/modules/account-island.js +232 -0
  72. package/lib/public/modules/admin-settings-email.js +197 -0
  73. package/lib/public/modules/admin-settings-invitations.js +183 -0
  74. package/lib/public/modules/admin-settings-projects.js +198 -0
  75. package/lib/public/modules/admin-settings-ui.js +100 -0
  76. package/lib/public/modules/admin-settings-users.js +331 -0
  77. package/lib/public/modules/admin.js +19 -949
  78. package/lib/public/modules/app-cursors.js +19 -43
  79. package/lib/public/modules/app-debate-ui.js +4 -3
  80. package/lib/public/modules/app-dm.js +0 -2
  81. package/lib/public/modules/app-header.js +106 -0
  82. package/lib/public/modules/app-message-router.js +12 -3
  83. package/lib/public/modules/app-messages.js +47 -61
  84. package/lib/public/modules/app-notifications.js +4 -7
  85. package/lib/public/modules/app-projects.js +5 -38
  86. package/lib/public/modules/app-rate-limit.js +70 -12
  87. package/lib/public/modules/app-rendering.js +12 -13
  88. package/lib/public/modules/avatar.js +30 -9
  89. package/lib/public/modules/chat-render-runtime.js +16 -4
  90. package/lib/public/modules/command-palette.js +2 -15
  91. package/lib/public/modules/context-sources.js +27 -18
  92. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  93. package/lib/public/modules/debate-controls.js +299 -0
  94. package/lib/public/modules/debate.js +2 -0
  95. package/lib/public/modules/debates-workbench.js +339 -0
  96. package/lib/public/modules/default-ai.js +88 -65
  97. package/lib/public/modules/header-tui-font.js +10 -20
  98. package/lib/public/modules/home-debate-controls.js +18 -289
  99. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  100. package/lib/public/modules/home-mate-color-picker.js +66 -0
  101. package/lib/public/modules/home-mate-creation.js +1 -1
  102. package/lib/public/modules/home-mate-instructions.js +90 -0
  103. package/lib/public/modules/home-mate-model-picker.js +80 -271
  104. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  105. package/lib/public/modules/home-mate-settings.js +118 -125
  106. package/lib/public/modules/home-shell.js +3 -0
  107. package/lib/public/modules/home-surface-boot.js +1 -9
  108. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  109. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  110. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  111. package/lib/public/modules/knowledge-history-state.js +55 -0
  112. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  113. package/lib/public/modules/knowledge-preview-links.js +70 -0
  114. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  115. package/lib/public/modules/linear-settings.js +3 -2
  116. package/lib/public/modules/markdown.js +5 -6
  117. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  118. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  119. package/lib/public/modules/mate-knowledge.js +11 -3
  120. package/lib/public/modules/mate-management.js +54 -10
  121. package/lib/public/modules/mate-sidebar.js +11 -1
  122. package/lib/public/modules/mate-workspace-color.js +33 -0
  123. package/lib/public/modules/mention.js +5 -13
  124. package/lib/public/modules/notification-settings.js +81 -0
  125. package/lib/public/modules/notifications.js +44 -28
  126. package/lib/public/modules/personal-settings-dialog.js +51 -0
  127. package/lib/public/modules/profile.js +66 -24
  128. package/lib/public/modules/project-chat-identity.js +79 -0
  129. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  130. package/lib/public/modules/project-debate-workspace.js +185 -0
  131. package/lib/public/modules/project-header-identity.js +127 -0
  132. package/lib/public/modules/project-logs-render.js +9 -4
  133. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  134. package/lib/public/modules/project-mate-navigation.js +238 -0
  135. package/lib/public/modules/project-session-navigation.js +29 -0
  136. package/lib/public/modules/project-settings-environment.js +106 -0
  137. package/lib/public/modules/project-settings.js +72 -628
  138. package/lib/public/modules/project-switcher.js +6 -8
  139. package/lib/public/modules/projects-hub-view.js +5 -6
  140. package/lib/public/modules/projects-hub.js +2 -6
  141. package/lib/public/modules/qrcode.js +27 -8
  142. package/lib/public/modules/search-clay-chat.js +8 -11
  143. package/lib/public/modules/server-settings-controls.js +316 -0
  144. package/lib/public/modules/server-settings-environment.js +194 -0
  145. package/lib/public/modules/server-settings-navigation.js +39 -0
  146. package/lib/public/modules/server-settings-state.js +54 -0
  147. package/lib/public/modules/server-settings.js +116 -788
  148. package/lib/public/modules/server-storage-settings.js +84 -0
  149. package/lib/public/modules/session-activity-tooltip.js +12 -0
  150. package/lib/public/modules/session-create-form.js +86 -14
  151. package/lib/public/modules/session-create.js +50 -7
  152. package/lib/public/modules/session-github.js +2 -1
  153. package/lib/public/modules/session-list-visibility.js +41 -0
  154. package/lib/public/modules/session-presence-ui.js +3 -0
  155. package/lib/public/modules/session-title-marquee.js +51 -0
  156. package/lib/public/modules/settings-env-values.js +34 -0
  157. package/lib/public/modules/settings-select.js +121 -0
  158. package/lib/public/modules/shared-browser.js +22 -12
  159. package/lib/public/modules/sidebar-mates.js +3 -50
  160. package/lib/public/modules/sidebar-mobile.js +63 -60
  161. package/lib/public/modules/sidebar-projects.js +84 -97
  162. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  163. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  164. package/lib/public/modules/sidebar-sessions.js +49 -50
  165. package/lib/public/modules/sidebar.js +11 -13
  166. package/lib/public/modules/stt-picker.js +14 -5
  167. package/lib/public/modules/terminal-prefs.js +22 -0
  168. package/lib/public/modules/theme.js +88 -52
  169. package/lib/public/modules/thinking-lifecycle.js +7 -6
  170. package/lib/public/modules/tool-palette-order.js +4 -3
  171. package/lib/public/modules/tool-palette.js +13 -3
  172. package/lib/public/modules/tools.js +22 -22
  173. package/lib/public/modules/user-settings-account.js +318 -0
  174. package/lib/public/modules/user-settings-appearance.js +55 -0
  175. package/lib/public/modules/user-settings-editors.js +24 -0
  176. package/lib/public/modules/user-settings-integrations.js +104 -0
  177. package/lib/public/modules/user-settings-layout.js +80 -0
  178. package/lib/public/modules/user-settings-preferences.js +48 -0
  179. package/lib/public/modules/user-settings-profile.js +53 -0
  180. package/lib/public/modules/user-settings-themes.js +38 -0
  181. package/lib/public/modules/user-settings-voice.js +75 -0
  182. package/lib/public/modules/user-settings.js +53 -480
  183. package/lib/public/modules/worker-proposal.js +42 -8
  184. package/lib/public/modules/worker-runtime-picker.js +115 -0
  185. package/lib/public/modules/you-activity.js +121 -0
  186. package/lib/public/modules/you.js +251 -0
  187. package/lib/public/style.css +20 -8
  188. package/lib/public/vendor/codemirror/LICENSE +21 -0
  189. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  190. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  191. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  192. package/lib/public/vendor/codemirror/markdown.js +886 -0
  193. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  194. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  195. package/lib/sdk-bridge.js +15 -3
  196. package/lib/sdk-message-processor.js +16 -4
  197. package/lib/sdk-quota-snapshot.js +37 -0
  198. package/lib/server-admin.js +11 -0
  199. package/lib/server-appearance-settings.js +71 -0
  200. package/lib/server-home-chat-events.js +1 -1
  201. package/lib/server-home-chat.js +25 -3
  202. package/lib/server-home-debate-planning.js +4 -1
  203. package/lib/server-home-debates.js +2 -0
  204. package/lib/server-home-mate-creation.js +4 -1
  205. package/lib/server-linear.js +14 -8
  206. package/lib/server-mate-instructions.js +44 -0
  207. package/lib/server-mates.js +14 -4
  208. package/lib/server-notification-settings.js +67 -0
  209. package/lib/server-palette.js +1 -1
  210. package/lib/server-settings.js +69 -1
  211. package/lib/server-storage.js +57 -0
  212. package/lib/server-user-knowledge.js +119 -0
  213. package/lib/server.js +31 -3
  214. package/lib/session-visibility.js +7 -2
  215. package/lib/sessions.js +35 -10
  216. package/lib/startup-projects.js +40 -0
  217. package/lib/storage-native-walk.js +85 -0
  218. package/lib/storage-usage.js +144 -0
  219. package/lib/user-knowledge-service.js +167 -0
  220. package/lib/users-permissions.js +7 -0
  221. package/lib/users-preferences.js +0 -74
  222. package/lib/users-project-mate-filter-preferences.js +39 -0
  223. package/lib/users.js +6 -4
  224. package/lib/workspace-query-access.js +4 -1
  225. package/lib/ws-schema.js +52 -4
  226. package/lib/yoke/acp-process-manager.js +2 -2
  227. package/lib/yoke/adapters/codex.js +12 -52
  228. package/lib/yoke/codex-app-server.js +2 -2
  229. package/lib/yoke/codex-model-catalog.js +52 -0
  230. package/lib/yoke/codex-rate-limits.js +26 -0
  231. package/package.json +12 -3
@@ -16,8 +16,7 @@
16
16
  import { store } from './store.js';
17
17
  import { refreshIcons } from './icons.js';
18
18
  import { getCachedProjects, switchProject } from './app-projects.js';
19
- import { showHomeHub, isHomeHubVisible } from './app-home-hub.js';
20
- import { openHomeChat } from './home-mate-chat.js';
19
+ import { openMateWorkspace, mateForWorkspace } from './project-mate-navigation.js';
21
20
  import { mateAvatarUrl } from './avatar.js';
22
21
  import { parseEmojis } from './markdown.js';
23
22
  import { isExternalWorktree, externalWorktreeTooltip, appendExternalWorktreeBadge } from './worktree-location.js';
@@ -34,7 +33,6 @@ var _entries = [];
34
33
 
35
34
  export function openSwitcherForMode(mode) {
36
35
  if (mode !== 'project' && mode !== 'mate') return;
37
- if (mode === 'mate' && !isHomeHubVisible()) return;
38
36
  if (_open) closeSwitcher(false);
39
37
  openSwitcher(mode);
40
38
  }
@@ -145,7 +143,6 @@ function handleKeyDown(e) {
145
143
  // same shortcut cycles.
146
144
  if (isMod && (k === 'e' || k === 'm')) {
147
145
  var requestedMode = k === 'e' ? 'project' : 'mate';
148
- if (requestedMode === 'mate' && !isHomeHubVisible()) return;
149
146
  e.preventDefault();
150
147
  e.stopPropagation();
151
148
  if (!_open) {
@@ -246,7 +243,8 @@ function pickInitialIndex() {
246
243
 
247
244
  function currentActiveKey() {
248
245
  if (_mode === 'mate') {
249
- return mateIdFrom(store.get('dmTargetUser'));
246
+ var mate = mateForWorkspace(store.snap());
247
+ return mate ? mate.id : null;
250
248
  }
251
249
  return store.get('currentSlug');
252
250
  }
@@ -277,7 +275,8 @@ function buildProjectEntries() {
277
275
 
278
276
  function buildMateEntries() {
279
277
  var mates = store.get('cachedMatesList') || [];
280
- var currentMateId = mateIdFrom(store.get('dmTargetUser'));
278
+ var current = mateForWorkspace(store.snap());
279
+ var currentMateId = current ? current.id : null;
281
280
  // Natural list order; MRU is only for choosing the initial highlight.
282
281
  return mates.map(function (m) {
283
282
  return {
@@ -302,8 +301,7 @@ function closeSwitcher(commit) {
302
301
  if (!entry || entry.disabled) return;
303
302
  if (_mode === 'mate') {
304
303
  if (entry.key !== currentActiveKey()) {
305
- showHomeHub();
306
- openHomeChat(entry.key);
304
+ openMateWorkspace(entry.key);
307
305
  }
308
306
  } else {
309
307
  if (entry.key !== store.get('currentSlug')) switchProject(entry.key);
@@ -18,12 +18,12 @@ function setupAction(mode, icon, title, text, disabled) {
18
18
 
19
19
  export function projectsHubContent(state) {
20
20
  var projects = ordinaryProjects(state.projectsHubList);
21
- var ready = state.projectListLoaded && state.homeSurfaceLoaded;
21
+ var ready = state.projectListLoaded;
22
22
  var welcome = ready && (state.projectsShowGuide || (!projects.length && (state.onboardingStep || 'welcome') === 'welcome'));
23
23
  var allowed = state.projectsAccessLoaded && (!state.permissions || state.permissions.createProject === true);
24
24
  var available = allowed && state.connected && !state.pendingHomeProjectSlug;
25
- var html = '<header class="projects-header"><a class="projects-brand" href="/" data-projects-action="home"><img src="/clay-studio-symbol.png" alt="">Clay Studio</a>' +
26
- '<nav aria-label="Workspace navigation">' + button('home', 'Home', 'projects-button-quiet') + button('settings', 'Settings', 'projects-button-quiet') + '</nav></header>';
25
+ var html = '<header class="projects-header"><a class="projects-brand" href="/projects"><img src="/clay-studio-symbol.png" alt="">Clay Studio</a>' +
26
+ '<nav aria-label="Workspace navigation">' + button('settings', 'Settings', 'projects-button-quiet') + '</nav></header>';
27
27
  html += '<main class="projects-main" aria-labelledby="projects-title">';
28
28
  if (!ready) return html + '<div class="projects-loading" role="status"><h1 id="projects-title" tabindex="-1">Your projects</h1><p>Loading your workspace…</p></div></main>';
29
29
  if (welcome) {
@@ -31,9 +31,8 @@ export function projectsHubContent(state) {
31
31
  '<h1 id="projects-title" tabindex="-1">A place for<br>your next idea.</h1>' +
32
32
  '<p class="projects-lead">Bring a project. Describe what you want to build.<br>Your AI Driver helps you take it from there.</p>' +
33
33
  '<div class="projects-intro-grid"><div><span>01 / PROJECTS</span><h2>Work on something real.</h2><p>Open a folder, start fresh, or clone a repository. Your conversations and tools stay with the project.</p></div>' +
34
- '<div><span>02 / HOME</span><h2>Make room to think.</h2><p>Talk with Clay and your Mates, explore ideas, and return to your projects whenever you’re ready.</p></div></div>' +
35
- '<div class="projects-welcome-actions">' + button('start', projects.length ? 'Explore your projects ' + iconHtml('arrow-right') : 'Set up your first project ' + iconHtml('arrow-right'), 'projects-button-primary') +
36
- button('skip', 'Explore Home first', 'projects-button-quiet') + '</div>' +
34
+ '<div><span>02 / MATES</span><h2>Bring in the right perspective.</h2><p>Your Mates live beside projects and open their conversations in the same focused workspace.</p></div></div>' +
35
+ '<div class="projects-welcome-actions">' + button('start', projects.length ? 'Explore your projects ' + iconHtml('arrow-right') : 'Set up your first project ' + iconHtml('arrow-right'), 'projects-button-primary') + '</div>' +
37
36
  '<p class="projects-footnote">You can revisit this guide from Projects at any time.</p></section></main>';
38
37
  }
39
38
  html += '<div class="projects-heading"><div><div class="projects-eyebrow">YOUR WORKSPACE</div><h1 id="projects-title" tabindex="-1">' + (projects.length ? 'Projects' : 'Start with a project.') + '</h1>' +
@@ -1,6 +1,5 @@
1
1
  import { store } from './store.js';
2
2
  import { getWs } from './ws-ref.js';
3
- import { hideHomeHub, showHomeHub } from './app-home-hub.js';
4
3
  import { openAddProjectModal, switchProject } from './app-projects.js';
5
4
  import { openUserSettings } from './user-settings.js';
6
5
  import { rememberHomePrimarySurface, updateHomeSurfacePreference } from './home-surface.js';
@@ -37,7 +36,6 @@ function focusHeading() {
37
36
  }
38
37
 
39
38
  export function showProjectsHub(fromHistory) {
40
- hideHomeHub();
41
39
  store.set({ projectsHubVisible: true, pendingHomeProjectSlug: null });
42
40
  document.body.classList.add('projects-active');
43
41
  root().classList.remove('hidden');
@@ -67,14 +65,12 @@ function handleClick(event) {
67
65
  if (!button || button.disabled) return;
68
66
  event.preventDefault();
69
67
  var action = button.dataset.projectsAction;
70
- if (action === 'home') { showHomeHub(); return; }
71
68
  if (action === 'settings') { openUserSettings(); return; }
72
69
  if (action === 'guide') { store.set({ projectsShowGuide: true }); render(); focusHeading(); return; }
73
- if (action === 'start' || action === 'skip') {
70
+ if (action === 'start') {
74
71
  store.set({ projectsShowGuide: false });
75
72
  updateHomeSurfacePreference({ onboardingStep: 'project' });
76
- if (action === 'skip') showHomeHub();
77
- else { render(); focusHeading(); }
73
+ render(); focusHeading();
78
74
  return;
79
75
  }
80
76
  var ws = getWs();
@@ -1,7 +1,18 @@
1
1
  import { copyToClipboard } from './utils.js';
2
2
 
3
- function getShareUrl() {
4
- var url = window.location.href;
3
+ var activeShareUrl = null;
4
+ var activeShareTitle = null;
5
+
6
+ export function getShareUrl(projectSlug) {
7
+ var locationUrl = new URL(window.location.href);
8
+ if (projectSlug) {
9
+ var projectIndex = locationUrl.pathname.indexOf("/p/");
10
+ var prefix = projectIndex === -1 ? "" : locationUrl.pathname.substring(0, projectIndex);
11
+ locationUrl.pathname = prefix + "/p/" + encodeURIComponent(projectSlug) + "/";
12
+ locationUrl.search = "";
13
+ locationUrl.hash = "";
14
+ }
15
+ var url = locationUrl.href;
5
16
  var h = window.location.hostname;
6
17
  if ((h === "localhost" || h === "127.0.0.1") && window.__lanHost) {
7
18
  url = url.replace(h + ":" + window.location.port, window.__lanHost);
@@ -9,13 +20,15 @@ function getShareUrl() {
9
20
  return url;
10
21
  }
11
22
 
12
- export function triggerShare() {
13
- var url = getShareUrl();
23
+ export function triggerShare(options) {
24
+ options = options || {};
25
+ var url = getShareUrl(options.projectSlug);
26
+ var title = options.title || document.title || "Clay";
14
27
 
15
28
  // Use Web Share API on mobile only
16
29
  var isMobile = window.innerWidth <= 768 || /Mobi|Android/i.test(navigator.userAgent);
17
30
  if (isMobile && navigator.share) {
18
- navigator.share({ title: document.title || "Clay", url: url }).catch(function () {});
31
+ navigator.share({ title: title, url: url }).catch(function () {});
19
32
  return;
20
33
  }
21
34
 
@@ -28,6 +41,8 @@ export function triggerShare() {
28
41
  var qr = qrcode(0, "M");
29
42
  qr.addData(url);
30
43
  qr.make();
44
+ activeShareUrl = url;
45
+ activeShareTitle = title;
31
46
  qrCanvas.innerHTML = qr.createSvgTag(5, 0);
32
47
  qrUrl.innerHTML = url + '<span class="qr-hint">click to copy</span>';
33
48
 
@@ -49,7 +64,7 @@ export function initQrCode() {
49
64
 
50
65
  // click URL to copy
51
66
  qrUrl.addEventListener("click", function () {
52
- var url = getShareUrl();
67
+ var url = activeShareUrl || getShareUrl();
53
68
  copyToClipboard(url).then(function () {
54
69
  qrUrl.innerHTML = "Copied!";
55
70
  qrUrl.classList.add("copied");
@@ -62,6 +77,8 @@ export function initQrCode() {
62
77
 
63
78
  qrOverlay.addEventListener("click", function () {
64
79
  qrOverlay.classList.add("hidden");
80
+ activeShareUrl = null;
81
+ activeShareTitle = null;
65
82
  });
66
83
 
67
84
  // prevent closing when clicking the inner card
@@ -73,8 +90,8 @@ export function initQrCode() {
73
90
  var qrShareBtn = document.getElementById("qr-share-btn");
74
91
  if (qrShareBtn) {
75
92
  qrShareBtn.addEventListener("click", function () {
76
- var url = getShareUrl();
77
- navigator.share({ title: document.title || "Clay", url: url }).catch(function () {});
93
+ var url = activeShareUrl || getShareUrl();
94
+ navigator.share({ title: activeShareTitle || document.title || "Clay", url: url }).catch(function () {});
78
95
  });
79
96
  }
80
97
 
@@ -82,6 +99,8 @@ export function initQrCode() {
82
99
  document.addEventListener("keydown", function (e) {
83
100
  if (e.key === "Escape" && !qrOverlay.classList.contains("hidden")) {
84
101
  qrOverlay.classList.add("hidden");
102
+ activeShareUrl = null;
103
+ activeShareTitle = null;
85
104
  }
86
105
  });
87
106
  }
@@ -1,7 +1,6 @@
1
1
  // Compact Clay conversation hosted inside the global search drawer.
2
2
  import { getWs } from './ws-ref.js';
3
- import { showHomeHub } from './app-home-hub.js';
4
- import { openHomeConversation } from './home-mate-chat.js';
3
+ import { openProjectSession } from './project-session-navigation.js';
5
4
  import { refreshIcons } from './icons.js';
6
5
  import { store } from './store.js';
7
6
  import { mateAvatarUrl } from './avatar.js';
@@ -231,12 +230,9 @@ function respondToPermission(permission, decision) {
231
230
  }
232
231
 
233
232
  function expand() {
234
- if (!state || !state.mateId || !state.sessionId) return;
235
- var mateId = state.mateId;
236
- var sessionId = state.sessionId;
233
+ if (!state || !state.mateId || !state.localId) return;
237
234
  if (closeHandler) closeHandler();
238
- showHomeHub();
239
- openHomeConversation(mateId, sessionId);
235
+ openProjectSession('mate-' + state.mateId, state.localId);
240
236
  }
241
237
 
242
238
  function render() {
@@ -259,7 +255,7 @@ function render() {
259
255
  var actions = document.createElement("div");
260
256
  actions.className = "search-clay-header-actions";
261
257
  actions.appendChild(button("search", "Search", "Back to search", function () { detachSearchClayChat(); if (backHandler) backHandler(); }));
262
- actions.appendChild(button("maximize-2", "Expand", "Open this conversation in Home", expand));
258
+ actions.appendChild(button("maximize-2", "Expand", "Open this conversation in project chat", expand));
263
259
  header.appendChild(actions);
264
260
  shell.appendChild(header);
265
261
  transcript = document.createElement("div");
@@ -318,8 +314,8 @@ function render() {
318
314
  sendButton.disabled = state.processing ? (!state.sessionId || state.stopping) : (!state.sessionId || !input.value.trim());
319
315
  sendButton.setAttribute("aria-label", state.processing ? (state.stopping ? "Stopping Clay" : "Stop Clay") : "Send to Clay");
320
316
  sendButton.innerHTML = state.processing ? '<i data-lucide="square" aria-hidden="true"></i>' : '<i data-lucide="arrow-up" aria-hidden="true"></i>';
321
- var expandButton = shell.querySelector('.search-clay-icon-button[aria-label="Open this conversation in Home"]');
322
- if (expandButton) expandButton.disabled = !state.sessionId;
317
+ var expandButton = shell.querySelector('.search-clay-icon-button[aria-label="Open this conversation in project chat"]');
318
+ if (expandButton) expandButton.disabled = !state.localId;
323
319
  refreshIcons();
324
320
  transcript.scrollTop = follow ? transcript.scrollHeight : previousTop;
325
321
  }
@@ -331,7 +327,7 @@ export function startSearchClayChat(query, host, onBack, onClose) {
331
327
  backHandler = onBack;
332
328
  closeHandler = onClose;
333
329
  var startedAt = Date.now();
334
- state = { requestId: requestId(), mateId: null, sessionId: null, messages: [{ role: "user", text: clean }], queuedUsers: [], pendingTurns: 1, activities: [], permissions: [], activityExpanded: false, stream: "", draft: "", processing: true, stopping: false, awaitingCompletion: true, activity: "thinking", step: 0, startedAt: startedAt, updatedAt: startedAt, query: clean };
330
+ state = { requestId: requestId(), mateId: null, sessionId: null, localId: null, messages: [{ role: "user", text: clean }], queuedUsers: [], pendingTurns: 1, activities: [], permissions: [], activityExpanded: false, stream: "", draft: "", processing: true, stopping: false, awaitingCompletion: true, activity: "thinking", step: 0, startedAt: startedAt, updatedAt: startedAt, query: clean };
335
331
  store.set({ searchClayChatState: state });
336
332
  startProgressTimer();
337
333
  render();
@@ -382,6 +378,7 @@ export function handleSearchClayMessage(msg) {
382
378
  updateActivity(msg);
383
379
  } else if (msg.type === "home_mate_history") {
384
380
  state.sessionId = msg.sessionId || state.sessionId;
381
+ state.localId = typeof msg.localId === "number" ? msg.localId : state.localId;
385
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;
386
383
  state.processing = msg.isProcessing === true;
387
384
  state.awaitingCompletion = state.processing;
@@ -0,0 +1,316 @@
1
+ import { store } from './store.js';
2
+ import { getWs } from './ws-ref.js';
3
+ import { showToast } from './utils.js';
4
+ import { confirmSettingsAction, clearSettingsDraft, setSettingsDraft } from './server-settings-state.js';
5
+ import { enhanceSettingsSelects } from './settings-select.js';
6
+
7
+ function status(id, text) {
8
+ var el = document.getElementById(id);
9
+ if (el) { el.textContent = text; el.classList.remove('hidden'); }
10
+ }
11
+
12
+ function sendSetting(type, values, buttonId, statusId) {
13
+ var ws = getWs();
14
+ if (!ws || ws.readyState !== 1) { status(statusId, 'Disconnected. Your change has not been saved.'); return; }
15
+ var button = document.getElementById(buttonId);
16
+ if (button) button.disabled = true;
17
+ var input = buttonId === 'settings-pin-save-btn' ? document.getElementById('settings-pin-input') : buttonId.endsWith('-save') ? document.getElementById(buttonId.slice(0, -5)) : null;
18
+ if (input) { input.disabled = true; input.dispatchEvent(new Event('settings-sync')); }
19
+ status(statusId, 'Saving…');
20
+ var pending = Object.assign({}, store.get('serverSettingsPending') || {});
21
+ pending[type] = { buttonId: buttonId, statusId: statusId };
22
+ store.set({ serverSettingsPending: pending });
23
+ ws.send(JSON.stringify(Object.assign({ type: type }, values)));
24
+ setTimeout(function () {
25
+ if ((store.get('serverSettingsPending') || {})[type] !== pending[type]) return;
26
+ if (button) button.disabled = false;
27
+ if (input) { input.disabled = false; input.dispatchEvent(new Event('settings-sync')); }
28
+ status(statusId, 'No acknowledgement received. Reconnect and retry; the server may have saved this value.');
29
+ }, 12000);
30
+ }
31
+
32
+ export function handleServerSettingResult(msg) {
33
+ var type = msg.type.replace(/_result$/, '');
34
+ var pending = Object.assign({}, store.get('serverSettingsPending') || {});
35
+ var request = pending[type];
36
+ if (!request) return;
37
+ delete pending[type];
38
+ store.set({ serverSettingsPending: pending });
39
+ var button = document.getElementById(request.buttonId);
40
+ if (button) { button.disabled = false; if (msg.ok && request.buttonId.endsWith('-save')) button.hidden = true; }
41
+ var input = request.buttonId === 'settings-pin-save-btn' ? document.getElementById('settings-pin-input') : request.buttonId.endsWith('-save') ? document.getElementById(request.buttonId.slice(0, -5)) : null;
42
+ if (input) { input.disabled = false; input.dispatchEvent(new Event('settings-sync')); }
43
+ status(request.statusId, msg.ok ? 'Saved. ' + (msg.timing || '') : (msg.error || 'Could not save. Retry your change.'));
44
+ if (msg.ok) clearSettingsDraft(type);
45
+ }
46
+
47
+ function finishSetting(msg, id, field) {
48
+ handleServerSettingResult(msg);
49
+ var el = document.getElementById(id);
50
+ var key = 'set_keep_awake';
51
+ if (!msg.type.endsWith('_result') && (store.get('serverSettingsDrafts') || {})[key]) return;
52
+ if (el && msg.ok !== false && msg[field] !== undefined) el.checked = !!msg[field];
53
+ }
54
+
55
+ function updatePlatformControls(config) {
56
+ store.set({ serverSettingsConfig: config });
57
+ document.getElementById('settings-keep-awake').disabled = config.platform !== 'darwin';
58
+ status('settings-awake-support', config.platform === 'darwin' ? 'Applies while Clay is running on this Mac.' : 'Unavailable on this platform. Keep awake requires macOS.');
59
+ var channel = document.getElementById('settings-update-channel');
60
+ if (channel && !(store.get('serverSettingsDrafts') || {}).set_update_channel) channel.checked = config.updateChannel === 'beta';
61
+ enhanceSettingsSelects(document.getElementById('server-settings'));
62
+ document.getElementById('settings-image-retention').dispatchEvent(new Event('settings-sync'));
63
+ }
64
+
65
+ export function initServerControls() {
66
+ ['settings-keep-awake', 'settings-image-retention', 'settings-update-channel', 'settings-pin-set-btn', 'settings-pin-remove-btn'].forEach(function (id) { document.getElementById(id).disabled = true; });
67
+ var check = document.getElementById('settings-update-check');
68
+ var update = document.createElement('button'); update.type = 'button'; update.className = 'settings-btn-sm'; update.id = 'settings-install-update'; update.hidden = true; update.textContent = 'Update and restart…';
69
+ check.insertAdjacentElement('afterend', update);
70
+ var receipt = document.createElement('p'); receipt.id = 'settings-update-status'; receipt.className = 'settings-hint'; receipt.setAttribute('role', 'status'); update.insertAdjacentElement('afterend', receipt);
71
+ update.onclick = function () {
72
+ confirmSettingsAction('Install the available release and restart Clay? Active work will be interrupted for everyone. Unsaved settings remain drafts in this tab.', 'Update and restart', function () {
73
+ var ws = getWs();
74
+ if (!ws || ws.readyState !== 1) { status('settings-update-status', 'Disconnected. Reconnect before updating.'); return; }
75
+ update.disabled = true; status('settings-update-status', 'Update requested. Waiting for the daemon…');
76
+ ws.send(JSON.stringify({ type: 'update_now' }));
77
+ });
78
+ };
79
+
80
+ document.getElementById('settings-pin-set-btn').onclick = showPinForm;
81
+ document.getElementById('settings-pin-cancel-btn').onclick = hidePinForm;
82
+ document.getElementById('settings-pin-save-btn').onclick = submitPin;
83
+ document.getElementById('settings-pin-input').oninput = function () { setSettingsDraft('pin', !!this.value); };
84
+ document.getElementById('settings-pin-remove-btn').onclick = function () {
85
+ confirmSettingsAction('Remove the server PIN? This changes sign-in protection for new connections.', 'Remove PIN', function () {
86
+ sendSetting('set_pin', { pin: null }, 'settings-pin-remove-btn', 'settings-pin-error');
87
+ });
88
+ };
89
+ [
90
+ ['settings-keep-awake', 'set_keep_awake', 'value', 'Saved changes apply now.'],
91
+ ['settings-image-retention', 'set_image_retention', 'days', 'Review retention. The next cleanup permanently deletes expired images.'],
92
+ ['settings-update-channel', 'set_update_channel', 'channel', 'Choose whether future update checks include prereleases.']
93
+ ].forEach(function (entry) {
94
+ var input = document.getElementById(entry[0]);
95
+ var receipt = document.createElement('p');
96
+ receipt.id = entry[0] + '-status'; receipt.className = 'settings-hint'; receipt.setAttribute('role', 'status');
97
+ input.closest('.settings-toggle-row').insertAdjacentElement('afterend', receipt);
98
+ var save = document.createElement('button'); save.type = 'button'; save.className = 'settings-btn-sm'; save.textContent = 'Save changes';
99
+ save.id = entry[0] + '-save'; save.hidden = true; receipt.insertAdjacentElement('beforebegin', save);
100
+ input.onchange = function () {
101
+ setSettingsDraft(entry[1], true); save.hidden = false; receipt.textContent = entry[3];
102
+ };
103
+ save.onclick = function () {
104
+ var values = {}; values[entry[2]] = entry[2] === 'days' ? Number(input.value) : entry[2] === 'channel' ? (input.checked ? 'beta' : 'stable') : input.checked;
105
+ sendSetting(entry[1], values, save.id, receipt.id);
106
+ };
107
+ });
108
+ document.getElementById('settings-restart-btn').onclick = function () {
109
+ confirmSettingsAction('Restart Clay for everyone? Active agent work will be interrupted. Clients will reconnect. Unsaved settings are not applied.', 'Restart server', function () {
110
+ sendSetting('restart_server', {}, 'settings-restart-btn', 'settings-restart-error');
111
+ });
112
+ };
113
+ var shutdown = document.getElementById('settings-shutdown-input');
114
+ var shutdownButton = document.getElementById('settings-shutdown-btn');
115
+ var reviewShutdown = document.getElementById('settings-shutdown-review');
116
+ var shutdownFlow = document.getElementById('settings-shutdown-flow');
117
+ reviewShutdown.onclick = function () {
118
+ store.set({ serverShutdownReview: true });
119
+ reviewShutdown.hidden = true;
120
+ reviewShutdown.setAttribute('aria-expanded', 'true');
121
+ shutdownFlow.hidden = false;
122
+ shutdown.focus();
123
+ };
124
+ document.getElementById('settings-shutdown-back').onclick = function () {
125
+ store.set({ serverShutdownReview: false });
126
+ shutdownFlow.hidden = true;
127
+ reviewShutdown.hidden = false;
128
+ reviewShutdown.setAttribute('aria-expanded', 'false');
129
+ shutdown.value = '';
130
+ shutdownButton.disabled = true;
131
+ reviewShutdown.focus();
132
+ };
133
+ shutdown.oninput = function () { shutdownButton.disabled = this.value.trim().toLowerCase() !== 'shutdown'; };
134
+ shutdownButton.onclick = function () {
135
+ if (!store.get('serverShutdownReview') || shutdown.value.trim().toLowerCase() !== 'shutdown') return;
136
+ confirmSettingsAction('Stop Clay for everyone? Active work stops and all clients disconnect. Start the service again from the host to recover.', 'Shut down server', function () {
137
+ sendSetting('shutdown_server', {}, shutdownButton.id, 'settings-shutdown-error');
138
+ });
139
+ };
140
+ }
141
+
142
+ export function updateDaemonConfig(config) {
143
+ if (config.error) { status('settings-access-status', config.error); return; }
144
+ store.set({ serverSettingsConfigLoaded: true });
145
+ ['settings-image-retention', 'settings-update-channel', 'settings-pin-set-btn', 'settings-pin-remove-btn'].forEach(function (id) { document.getElementById(id).disabled = false; });
146
+ // Nav header: show hostname (strip .local suffix, lowercase)
147
+ var nameEl = document.getElementById("settings-server-name");
148
+ if (nameEl && config.hostname) {
149
+ var displayHost = config.hostname.replace(/\.local$/i, "").toLowerCase();
150
+ nameEl.textContent = displayHost;
151
+ nameEl.title = config.hostname;
152
+ }
153
+
154
+ // Host
155
+ var hostnameEl = document.getElementById("settings-hostname");
156
+ var lanIpEl = document.getElementById("settings-lan-ip");
157
+ if (hostnameEl) hostnameEl.textContent = config.hostname || "-";
158
+ if (lanIpEl) lanIpEl.textContent = config.lanIp || "";
159
+
160
+ // Port
161
+ var portEl = document.getElementById("settings-port");
162
+ if (portEl) portEl.textContent = String(config.port || "-");
163
+
164
+ // TLS
165
+ var tlsEl = document.getElementById("settings-tls");
166
+ if (tlsEl) {
167
+ tlsEl.textContent = config.tls ? "Enabled" : "Disabled";
168
+ tlsEl.classList.toggle("settings-badge-green", !!config.tls);
169
+ }
170
+
171
+ // Debug
172
+ var debugEl = document.getElementById("settings-debug");
173
+ if (debugEl) {
174
+ debugEl.textContent = config.debug ? "Enabled" : "Disabled";
175
+ debugEl.classList.toggle("settings-badge-on", !!config.debug);
176
+ }
177
+
178
+ // PIN status
179
+ updatePinStatus(!!config.pinEnabled);
180
+
181
+ // Auto-continue on rate limit
182
+ // Auto-continue is now per-user (User Settings > Behavior)
183
+
184
+ // Keep awake
185
+ var keepAwakeToggle = document.getElementById("settings-keep-awake");
186
+ if (keepAwakeToggle && !(store.get('serverSettingsDrafts') || {}).set_keep_awake) keepAwakeToggle.checked = !!config.keepAwake;
187
+
188
+ // Image retention
189
+ var imageRetentionSelect = document.getElementById("settings-image-retention");
190
+ if (imageRetentionSelect && !(store.get('serverSettingsDrafts') || {}).set_image_retention && config.imageRetentionDays !== undefined) {
191
+ imageRetentionSelect.value = String(config.imageRetentionDays);
192
+ }
193
+
194
+ updatePlatformControls(config);
195
+
196
+ }
197
+
198
+ export function handleSetPinResult(msg) {
199
+ if (msg.ok) {
200
+ updatePinStatus(!!msg.pinEnabled);
201
+ hidePinForm();
202
+ showToast(msg.pinEnabled ? "PIN saved" : "PIN removed");
203
+ clearSettingsDraft("pin");
204
+ } else { status("settings-pin-error", msg.error || "Could not save PIN. Your draft is retained."); }
205
+ document.getElementById("settings-pin-save-btn").disabled = false;
206
+ }
207
+
208
+ export function handleKeepAwakeChanged(msg) {
209
+ finishSetting(msg, "settings-keep-awake", "keepAwake");
210
+ }
211
+
212
+ export function handleAutoContinueChanged(msg) {
213
+ // Auto-continue is now per-user; server broadcast no longer updates UI
214
+ }
215
+
216
+ export function handleRestartResult(msg) {
217
+ var restartBtn = document.getElementById("settings-restart-btn");
218
+ var errorEl = document.getElementById("settings-restart-error");
219
+
220
+ if (msg.ok) {
221
+ if (restartBtn) { restartBtn.textContent = "Server restarting..."; restartBtn.disabled = true; }
222
+ showToast("Server is restarting...");
223
+ } else {
224
+ if (restartBtn) {
225
+ restartBtn.textContent = "Restart";
226
+ restartBtn.disabled = false;
227
+ }
228
+ if (errorEl) {
229
+ errorEl.textContent = msg.error || "Restart failed";
230
+ errorEl.classList.remove("hidden");
231
+ }
232
+ }
233
+ }
234
+
235
+ export function handleShutdownResult(msg) {
236
+ var shutdownInput = document.getElementById("settings-shutdown-input");
237
+ var shutdownBtn = document.getElementById("settings-shutdown-btn");
238
+ var errorEl = document.getElementById("settings-shutdown-error");
239
+
240
+ if (msg.ok) {
241
+ if (shutdownBtn) { shutdownBtn.textContent = "Server stopping..."; shutdownBtn.disabled = true; }
242
+ showToast("Server is shutting down...");
243
+ } else {
244
+ if (shutdownBtn) {
245
+ shutdownBtn.textContent = "Shut down…";
246
+ shutdownBtn.disabled = false;
247
+ }
248
+ if (shutdownInput) shutdownInput.disabled = false;
249
+ if (errorEl) {
250
+ errorEl.textContent = msg.error || "Shutdown failed";
251
+ errorEl.classList.remove("hidden");
252
+ }
253
+ }
254
+ }
255
+
256
+ // --- PIN form management ---
257
+ function showPinForm() {
258
+ var form = document.getElementById("settings-pin-form");
259
+ var input = document.getElementById("settings-pin-input");
260
+ var errorEl = document.getElementById("settings-pin-error");
261
+ if (form) form.classList.remove("hidden");
262
+ if (errorEl) errorEl.classList.add("hidden");
263
+ if (input) input.focus();
264
+ }
265
+
266
+ function hidePinForm() {
267
+ var form = document.getElementById("settings-pin-form");
268
+ var input = document.getElementById("settings-pin-input");
269
+ var errorEl = document.getElementById("settings-pin-error");
270
+ clearSettingsDraft("pin");
271
+ if (form) form.classList.add("hidden");
272
+ if (input) input.value = "";
273
+ if (errorEl) errorEl.classList.add("hidden");
274
+ }
275
+
276
+ function submitPin() {
277
+ var input = document.getElementById("settings-pin-input");
278
+ var errorEl = document.getElementById("settings-pin-error");
279
+ if (!input) return;
280
+ var pin = input.value.trim();
281
+ if (!/^\d{6}$/.test(pin)) {
282
+ if (errorEl) { errorEl.textContent = "Enter exactly six digits."; errorEl.classList.remove("hidden"); }
283
+ input.focus();
284
+ return;
285
+ }
286
+ if (errorEl) errorEl.classList.add("hidden");
287
+ var ws = getWs();
288
+ if (ws && ws.readyState === 1) {
289
+ sendSetting("set_pin", { pin: pin }, "settings-pin-save-btn", "settings-pin-error");
290
+ } else { status("settings-pin-error", "Disconnected. Reconnect and save again."); }
291
+ }
292
+
293
+ function updatePinStatus(enabled) {
294
+ var statusEl = document.getElementById("settings-pin-status");
295
+ var setBtn = document.getElementById("settings-pin-set-btn");
296
+ var removeBtn = document.getElementById("settings-pin-remove-btn");
297
+ var actionLabel = document.getElementById("settings-pin-action-label");
298
+
299
+ if (statusEl) {
300
+ statusEl.textContent = enabled ? "Enabled" : "Disabled";
301
+ statusEl.classList.toggle("settings-badge-green", enabled);
302
+ }
303
+ if (setBtn) setBtn.textContent = enabled ? "Change PIN" : "Set PIN";
304
+ if (removeBtn) removeBtn.classList.toggle("hidden", !enabled);
305
+ if (actionLabel) actionLabel.textContent = enabled ? "Change PIN" : "Set PIN";
306
+ }
307
+
308
+
309
+ export function handleServerUpdate(msg) {
310
+ var button = document.getElementById('settings-install-update');
311
+ if (!button) return;
312
+ if (msg.type === 'update_available') { button.hidden = false; button.disabled = false; status('settings-update-status', 'Version ' + msg.version + ' is available. Review and restart when ready.'); }
313
+ if (msg.type === 'up_to_date') { button.hidden = true; status('settings-update-status', 'No newer release is available on this channel.'); }
314
+ if (msg.type === 'update_started') { button.disabled = true; status('settings-update-status', 'Updating. The connection will recover after the daemon restarts.'); }
315
+ if (msg.type === 'update_check_failed') { document.getElementById('settings-update-check').disabled = false; status('settings-update-status', msg.error || 'Update check failed. Retry when connected.'); }
316
+ }