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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/LICENSES/README.md +4 -0
  2. package/LICENSES/dicebear-avatars.md +16 -0
  3. package/lib/daemon-project-access.js +7 -1
  4. package/lib/daemon.js +2 -0
  5. package/lib/dicebear-avatar.js +62 -0
  6. package/lib/knowledge-db-service.js +175 -0
  7. package/lib/knowledge-db-validation.js +0 -0
  8. package/lib/knowledge-document-index.js +49 -0
  9. package/lib/knowledge-draft-store.js +56 -0
  10. package/lib/knowledge-file-store.js +199 -0
  11. package/lib/knowledge-markdown.js +128 -0
  12. package/lib/mate-knowledge-mcp-server.js +29 -6
  13. package/lib/mate-knowledge-service.js +23 -2
  14. package/lib/mate-knowledge-sync.js +36 -21
  15. package/lib/mate-workspace-color.js +4 -0
  16. package/lib/mates.js +18 -0
  17. package/lib/project-connection.js +36 -2
  18. package/lib/project-debate-proposal.js +6 -0
  19. package/lib/project-debate-workspace.js +43 -0
  20. package/lib/project-debate.js +1 -0
  21. package/lib/project-knowledge.js +315 -136
  22. package/lib/project-mate-creation-proposal.js +1 -0
  23. package/lib/project-sessions.js +39 -2
  24. package/lib/project-user-knowledge.js +60 -0
  25. package/lib/project.js +59 -18
  26. package/lib/public/app.js +73 -34
  27. package/lib/public/clay-studio-wordmark-bold.svg +20 -0
  28. package/lib/public/css/clay-workspace.css +105 -0
  29. package/lib/public/css/debates-workbench.css +47 -0
  30. package/lib/public/css/home-debate-live.css +26 -0
  31. package/lib/public/css/home-hub.css +3 -1
  32. package/lib/public/css/home-mate-model-picker.css +15 -131
  33. package/lib/public/css/home-mate-settings.css +168 -16
  34. package/lib/public/css/icon-strip.css +249 -32
  35. package/lib/public/css/mate-conversation.css +176 -0
  36. package/lib/public/css/mate-knowledge-workbench.css +118 -0
  37. package/lib/public/css/mate-workspace-color.css +16 -0
  38. package/lib/public/css/mates.css +28 -28
  39. package/lib/public/css/messages.css +1 -114
  40. package/lib/public/css/mobile-nav.css +16 -16
  41. package/lib/public/css/overlays.css +8 -0
  42. package/lib/public/css/project-logs.css +7 -0
  43. package/lib/public/css/project-mate-creation.css +105 -0
  44. package/lib/public/css/right-workbench.css +3 -3
  45. package/lib/public/css/session-folders.css +80 -21
  46. package/lib/public/css/sidebar.css +183 -107
  47. package/lib/public/css/title-bar.css +151 -0
  48. package/lib/public/css/worker-delegation.css +47 -0
  49. package/lib/public/css/worker-proposal.css +72 -150
  50. package/lib/public/css/you.css +74 -0
  51. package/lib/public/index.html +33 -101
  52. package/lib/public/modules/account-island.js +230 -0
  53. package/lib/public/modules/app-cursors.js +19 -43
  54. package/lib/public/modules/app-debate-ui.js +4 -3
  55. package/lib/public/modules/app-header.js +106 -0
  56. package/lib/public/modules/app-message-router.js +12 -3
  57. package/lib/public/modules/app-messages.js +34 -9
  58. package/lib/public/modules/app-notifications.js +4 -7
  59. package/lib/public/modules/app-projects.js +5 -38
  60. package/lib/public/modules/app-rendering.js +12 -13
  61. package/lib/public/modules/avatar.js +25 -9
  62. package/lib/public/modules/chat-render-runtime.js +16 -4
  63. package/lib/public/modules/command-palette.js +2 -15
  64. package/lib/public/modules/cursor-sharing-preference.js +3 -3
  65. package/lib/public/modules/debate-controls.js +299 -0
  66. package/lib/public/modules/debate.js +2 -0
  67. package/lib/public/modules/debates-workbench.js +339 -0
  68. package/lib/public/modules/default-ai.js +81 -65
  69. package/lib/public/modules/home-debate-controls.js +18 -289
  70. package/lib/public/modules/home-mate-avatar-editor.js +228 -0
  71. package/lib/public/modules/home-mate-color-picker.js +66 -0
  72. package/lib/public/modules/home-mate-creation.js +1 -1
  73. package/lib/public/modules/home-mate-instructions.js +90 -0
  74. package/lib/public/modules/home-mate-model-picker.js +80 -271
  75. package/lib/public/modules/home-mate-profile-editor.js +72 -0
  76. package/lib/public/modules/home-mate-settings.js +118 -125
  77. package/lib/public/modules/home-shell.js +3 -0
  78. package/lib/public/modules/home-surface-boot.js +1 -9
  79. package/lib/public/modules/icon-strip-tooltip.js +95 -0
  80. package/lib/public/modules/knowledge-db-edit-state.js +35 -0
  81. package/lib/public/modules/knowledge-db-workbench.js +234 -0
  82. package/lib/public/modules/knowledge-history-state.js +55 -0
  83. package/lib/public/modules/knowledge-markdown-editor.js +57 -0
  84. package/lib/public/modules/knowledge-preview-links.js +70 -0
  85. package/lib/public/modules/knowledge-workbench-shell.js +21 -0
  86. package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
  87. package/lib/public/modules/mate-knowledge-workbench.js +500 -0
  88. package/lib/public/modules/mate-knowledge.js +11 -3
  89. package/lib/public/modules/mate-management.js +54 -10
  90. package/lib/public/modules/mate-sidebar.js +11 -1
  91. package/lib/public/modules/mate-workspace-color.js +33 -0
  92. package/lib/public/modules/mention.js +5 -13
  93. package/lib/public/modules/profile.js +51 -18
  94. package/lib/public/modules/project-chat-identity.js +79 -0
  95. package/lib/public/modules/project-debate-planning-workspace.js +250 -0
  96. package/lib/public/modules/project-debate-workspace.js +185 -0
  97. package/lib/public/modules/project-header-identity.js +127 -0
  98. package/lib/public/modules/project-logs-render.js +9 -4
  99. package/lib/public/modules/project-mate-creation-workspace.js +414 -0
  100. package/lib/public/modules/project-mate-navigation.js +238 -0
  101. package/lib/public/modules/project-session-navigation.js +29 -0
  102. package/lib/public/modules/project-switcher.js +6 -8
  103. package/lib/public/modules/projects-hub-view.js +5 -6
  104. package/lib/public/modules/projects-hub.js +2 -6
  105. package/lib/public/modules/qrcode.js +27 -8
  106. package/lib/public/modules/search-clay-chat.js +8 -11
  107. package/lib/public/modules/session-activity-tooltip.js +12 -0
  108. package/lib/public/modules/session-create-form.js +86 -14
  109. package/lib/public/modules/session-create.js +50 -7
  110. package/lib/public/modules/session-list-visibility.js +41 -0
  111. package/lib/public/modules/session-presence-ui.js +4 -1
  112. package/lib/public/modules/session-title-marquee.js +51 -0
  113. package/lib/public/modules/sidebar-mates.js +3 -50
  114. package/lib/public/modules/sidebar-mobile.js +63 -60
  115. package/lib/public/modules/sidebar-projects.js +84 -97
  116. package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
  117. package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
  118. package/lib/public/modules/sidebar-sessions.js +49 -50
  119. package/lib/public/modules/sidebar.js +11 -13
  120. package/lib/public/modules/thinking-lifecycle.js +7 -6
  121. package/lib/public/modules/tool-palette-order.js +4 -3
  122. package/lib/public/modules/tool-palette.js +13 -3
  123. package/lib/public/modules/tools.js +22 -22
  124. package/lib/public/modules/worker-proposal.js +42 -8
  125. package/lib/public/modules/worker-runtime-picker.js +115 -0
  126. package/lib/public/modules/you-activity.js +121 -0
  127. package/lib/public/modules/you.js +251 -0
  128. package/lib/public/style.css +16 -8
  129. package/lib/public/vendor/codemirror/LICENSE +21 -0
  130. package/lib/public/vendor/codemirror/codemirror.css +344 -0
  131. package/lib/public/vendor/codemirror/codemirror.js +9884 -0
  132. package/lib/public/vendor/codemirror/continuelist.js +101 -0
  133. package/lib/public/vendor/codemirror/markdown.js +886 -0
  134. package/lib/public/vendor/codemirror/show-hint.css +37 -0
  135. package/lib/public/vendor/codemirror/show-hint.js +523 -0
  136. package/lib/sdk-bridge.js +15 -3
  137. package/lib/server-admin.js +11 -0
  138. package/lib/server-home-chat-events.js +1 -1
  139. package/lib/server-home-chat.js +25 -3
  140. package/lib/server-home-debate-planning.js +4 -1
  141. package/lib/server-home-debates.js +2 -0
  142. package/lib/server-home-mate-creation.js +4 -1
  143. package/lib/server-mate-instructions.js +44 -0
  144. package/lib/server-mates.js +12 -3
  145. package/lib/server-palette.js +1 -1
  146. package/lib/server-settings.js +65 -1
  147. package/lib/server-user-knowledge.js +119 -0
  148. package/lib/server.js +21 -1
  149. package/lib/session-visibility.js +7 -2
  150. package/lib/sessions.js +32 -6
  151. package/lib/user-knowledge-service.js +167 -0
  152. package/lib/users-permissions.js +7 -0
  153. package/lib/users-project-mate-filter-preferences.js +39 -0
  154. package/lib/users.js +6 -0
  155. package/lib/workspace-query-access.js +4 -1
  156. package/lib/ws-schema.js +50 -0
  157. package/package.json +10 -1
@@ -1,10 +1,15 @@
1
- // Centered General/Model/Memory/Knowledge settings dialog.
1
+ // Centered General/Model/Prompt settings dialog.
2
2
 
3
+ import { renderMateColorPicker, resetMateColor, mateColorSaving, applyMateColorResult } from './home-mate-color-picker.js';
4
+ import { resetMateProfile, hasMateProfileDraft, mateProfileSaving, applyMateProfile, renderMateProfile } from './home-mate-profile-editor.js';
5
+ import { showConfirm } from './app-misc.js';
6
+ import { resetMateInstructions, loadMateInstructions, renderMateInstructions, applyMateInstructions, hasMateInstructionDraft } from './home-mate-instructions.js';
3
7
  import { store } from './store.js';
4
8
  import { getWs } from './ws-ref.js';
5
9
  import { iconHtml, refreshIcons } from './icons.js';
6
- import { editMateProfile, confirmMateRemoval } from './mate-management.js';
7
- import { resetHomeMateModelPicker, clearHomeMateModelPicker, requestHomeMateModels, renderHomeMateModelPicker, applyHomeMateModelsState, applyHomeMateModelResult } from './home-mate-model-picker.js';
10
+ import { confirmMateRemoval } from './mate-management.js';
11
+ import { renderHomeMateAvatarEditor, confirmHomeMateAvatar, failHomeMateAvatar, clearHomeMateAvatarEditor } from './home-mate-avatar-editor.js';
12
+ import { resetHomeMateModelPicker, clearHomeMateModelPicker, hasHomeMateModelDraft, requestHomeMateModels, renderHomeMateModelPicker, applyHomeMateModelsState, applyHomeMateModelResult } from './home-mate-model-picker.js';
8
13
 
9
14
  var dialog = null;
10
15
  var dialogOpener = null;
@@ -52,31 +57,7 @@ function setTransientDrawerMask(masked) {
52
57
  }
53
58
 
54
59
  function focusableElements(root) {
55
- return root ? root.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])') : [];
56
- }
57
-
58
- function restoreModelFocusAfterRender() {
59
- var active = document.activeElement;
60
- var focusedModel = active && active.dataset ? active.dataset.homeMateModel : null;
61
- var focusedVendor = active && active.dataset ? active.dataset.homeMateVendor : null;
62
- var focusedControl = active && active.dataset ? active.dataset.homeModelFocus : null;
63
- return function () {
64
- if (!dialog) return;
65
- var selectors = [
66
- { selector: "[data-home-mate-model]", key: "homeMateModel", value: focusedModel },
67
- { selector: "[data-home-mate-vendor]", key: "homeMateVendor", value: focusedVendor },
68
- { selector: "[data-home-model-focus]", key: "homeModelFocus", value: focusedControl },
69
- ];
70
- for (var i = 0; i < selectors.length; i++) {
71
- if (!selectors[i].value) continue;
72
- var nodes = dialog.querySelectorAll(selectors[i].selector);
73
- for (var j = 0; j < nodes.length; j++) {
74
- if (nodes[j].dataset[selectors[i].key] !== selectors[i].value) continue;
75
- nodes[j].focus({ preventScroll: true });
76
- return;
77
- }
78
- }
79
- };
60
+ return root ? Array.from(root.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter(function (el) { return el.getClientRects().length && el.tabIndex >= 0; }) : [];
80
61
  }
81
62
 
82
63
  function addEmpty(container, text) {
@@ -87,30 +68,30 @@ function addEmpty(container, text) {
87
68
  }
88
69
 
89
70
  function renderGeneral(body, mate) {
90
- var profile = mate && mate.profile ? mate.profile : {};
91
- var identity = document.createElement("section");
92
- identity.className = "home-mate-settings-identity";
93
- var eyebrow = document.createElement("span");
94
- eyebrow.textContent = mate && mate.primary ? "Primary Mate" : mate && mate.builtinKey ? "Built-in Mate" : "Custom Mate";
95
- var name = document.createElement("h3");
96
- name.textContent = getMateName(mate);
97
- var bio = document.createElement("p");
98
- bio.textContent = profile.bio || (mate && mate.bio) || "Profile and management controls for this Mate.";
99
- identity.appendChild(eyebrow);
100
- identity.appendChild(name);
101
- identity.appendChild(bio);
102
- body.appendChild(identity);
71
+ var description = document.createElement("p");
72
+ description.className = "mate-settings-help";
73
+ description.textContent = "Manage " + getMateName(mate) + "’s identity and appearance.";
74
+ body.appendChild(description);
75
+ renderMateProfile(body, mate, renderDialogContent, function () {
76
+ store.set({mateSettingsAvatarOpen: !store.get('mateSettingsAvatarOpen')});
77
+ renderDialogContent();
78
+ });
79
+ if (store.get('mateSettingsAvatarOpen')) {
80
+ var appearance = document.createElement("section");
81
+ appearance.className = "mate-settings-appearance";
82
+ renderHomeMateAvatarEditor(appearance, mate, renderDialogContent);
83
+ var done = document.createElement('button'); done.type = 'button'; done.className = 'mate-settings-button'; done.textContent = 'Done';
84
+ done.addEventListener('click',function () { store.set({mateSettingsAvatarOpen:false}); renderDialogContent(); document.getElementById('mate-profile-avatar').focus(); });
85
+ appearance.appendChild(done);
86
+ body.appendChild(appearance);
87
+ }
88
+ renderMateColorPicker(body, mate, renderDialogContent);
103
89
  if (!mate || mate.primary) {
104
- addEmpty(body, "This primary Mate is managed by Clay.");
90
+ addEmpty(body, "Clay manages this Mate’s core profile.");
105
91
  return;
106
92
  }
107
93
  var actions = document.createElement("div");
108
94
  actions.className = "home-mate-settings-actions";
109
- var edit = document.createElement("button");
110
- edit.type = "button";
111
- edit.className = "home-mate-settings-action";
112
- edit.innerHTML = iconHtml("edit-2") + "<span>Edit profile</span>";
113
- edit.addEventListener("click", function () { editMateProfile(edit, mate); });
114
95
  var remove = document.createElement("button");
115
96
  remove.type = "button";
116
97
  remove.className = "home-mate-settings-action is-danger";
@@ -119,90 +100,46 @@ function renderGeneral(body, mate) {
119
100
  removeLabel.textContent = mate.builtinKey ? "Remove Mate" : "Delete Mate";
120
101
  remove.appendChild(removeLabel);
121
102
  remove.addEventListener("click", function () { confirmMateRemoval(remove, mate, closeHomeMateSettings); });
122
- actions.appendChild(edit);
103
+ var dangerCopy = document.createElement("div");
104
+ dangerCopy.className = "mate-settings-danger-copy";
105
+ dangerCopy.innerHTML = "<h4>Danger zone</h4><p>Permanently delete this Mate and its workspace data. This cannot be undone.</p>";
106
+ actions.appendChild(dangerCopy);
123
107
  actions.appendChild(remove);
124
108
  body.appendChild(actions);
125
109
  }
126
110
 
127
- function renderMemory(body) {
128
- if (!memoryState) {
129
- addEmpty(body, "Loading memory…");
130
- return;
131
- }
132
- if (memoryState.summary) {
133
- var summary = document.createElement("section");
134
- summary.className = "home-mate-settings-summary";
135
- var heading = document.createElement("h3");
136
- heading.textContent = "Working summary";
137
- var text = document.createElement("p");
138
- text.textContent = memoryState.summary;
139
- summary.appendChild(heading);
140
- summary.appendChild(text);
141
- body.appendChild(summary);
142
- }
143
- var entries = memoryState.entries || [];
144
- for (var i = 0; i < entries.length; i++) {
145
- var item = document.createElement("article");
146
- item.className = "home-mate-settings-memory";
147
- var title = document.createElement("h3");
148
- title.textContent = entries[i].topic || entries[i].date || "Memory";
149
- var detail = document.createElement("p");
150
- detail.textContent = entries[i].my_position || entries[i].decisions || entries[i].outcome || "";
151
- item.appendChild(title);
152
- if (detail.textContent) item.appendChild(detail);
153
- body.appendChild(item);
154
- }
155
- if (!memoryState.summary && !entries.length) addEmpty(body, "No memories yet.");
156
- }
157
-
158
- function renderKnowledge(body) {
159
- if (!knowledgeState) {
160
- addEmpty(body, "Loading knowledge…");
161
- return;
162
- }
163
- var files = knowledgeState.files || [];
164
- for (var i = 0; i < files.length; i++) {
165
- var item = document.createElement("div");
166
- item.className = "home-mate-settings-file";
167
- item.innerHTML = iconHtml("file-text");
168
- var name = document.createElement("span");
169
- name.textContent = typeof files[i] === "string" ? files[i] : files[i].name || "Untitled";
170
- item.appendChild(name);
171
- body.appendChild(item);
172
- }
173
- if (!files.length) addEmpty(body, "No knowledge files yet.");
174
- }
175
-
176
111
  function requestSection(section) {
177
112
  if (section === "model" && !modelRequested) {
178
113
  modelRequested = true;
179
- var mate = getMate(dialogMateId);
180
- requestHomeMateModels((mate || {}).vendor || "", renderDialogContent, false);
181
- }
182
- if (section === "memory") {
183
- memoryState = null;
184
- memoryRequestId = nextRequestId("memory");
185
- send({ type: "home_mate_memory_list", mateId: dialogMateId, requestId: memoryRequestId });
186
- }
187
- if (section === "knowledge") {
188
- knowledgeState = null;
189
- knowledgeRequestId = nextRequestId("knowledge");
190
- send({ type: "home_mate_knowledge_list", mateId: dialogMateId, requestId: knowledgeRequestId });
114
+ requestHomeMateModels((getMate(dialogMateId) || {}).vendor || "", renderDialogContent);
191
115
  }
192
116
  }
193
117
 
118
+ function savePending() {
119
+ var prompt = store.get('mateInstructionsDraft');
120
+ var model = store.get('mateModelDraft');
121
+ return mateColorSaving() || mateProfileSaving() || !!((prompt && prompt.status === 'saving') || (model && model.saveId));
122
+ }
123
+
194
124
  function renderDialogContent() {
195
125
  if (!dialog) return;
126
+ dialog.dataset.section = dialogSection;
196
127
  var body = dialog.querySelector(".home-mate-settings-body");
197
128
  if (!body) return;
129
+ var focus = document.activeElement;
130
+ var focusId = focus && focus.id;
131
+ var ownedFocus = focus && body.contains(focus);
132
+ var selection = focus && focus.tagName === 'TEXTAREA' ? [focus.selectionStart, focus.selectionEnd] : null;
133
+ var scroll = body.parentElement.scrollTop;
134
+ var expanded = body.querySelector('.mate-settings-appearance');
135
+ var appearanceOpen = expanded && expanded.open;
198
136
  body.innerHTML = "";
199
137
  var mate = getMate(dialogMateId);
200
138
  var contentTitle = dialog.querySelector(".home-mate-settings-content-title");
201
- if (contentTitle) contentTitle.textContent = dialogSection.charAt(0).toUpperCase() + dialogSection.slice(1) + " settings";
139
+ if (contentTitle) contentTitle.textContent = dialogSection.charAt(0).toUpperCase() + dialogSection.slice(1);
202
140
  if (dialogSection === "general") renderGeneral(body, mate);
203
141
  else if (dialogSection === "model") renderHomeMateModelPicker(body, renderDialogContent);
204
- else if (dialogSection === "memory") renderMemory(body);
205
- else renderKnowledge(body);
142
+ else renderMateInstructions(body, renderDialogContent);
206
143
  var nav = dialog.querySelectorAll("[data-home-mate-settings-section]");
207
144
  for (var i = 0; i < nav.length; i++) {
208
145
  var active = nav[i].dataset.homeMateSettingsSection === dialogSection;
@@ -211,10 +148,22 @@ function renderDialogContent() {
211
148
  else nav[i].removeAttribute("aria-current");
212
149
  }
213
150
  refreshIcons();
151
+ dialog.querySelector('.home-mate-settings-close').disabled = savePending();
152
+ var appearance = body.querySelector('.mate-settings-appearance');
153
+ if (appearance && appearanceOpen) appearance.open = true;
154
+ body.parentElement.scrollTop = scroll;
155
+ var nextFocus = focusId && document.getElementById(focusId);
156
+ if (nextFocus && !nextFocus.disabled) {
157
+ nextFocus.focus({preventScroll:true});
158
+ if (selection) nextFocus.setSelectionRange(selection[0], selection[1]);
159
+ } else if (ownedFocus) {
160
+ var fallback = body.querySelector('#mate-prompt-input:not(:disabled), #mate-settings-model-trigger:not(:disabled)') || contentTitle;
161
+ if (fallback) fallback.focus({preventScroll:true});
162
+ }
214
163
  }
215
164
 
216
165
  function selectSection(section, focusContent) {
217
- if (section !== "general" && section !== "model" && section !== "memory" && section !== "knowledge") return;
166
+ if (section !== "general" && section !== "model" && section !== "prompt") return;
218
167
  dialogSection = section;
219
168
  requestSection(section);
220
169
  renderDialogContent();
@@ -227,6 +176,7 @@ function selectSection(section, focusContent) {
227
176
  function handleDialogKeydown(event) {
228
177
  if (!dialog) return;
229
178
  if (document.querySelector(".profile-popover") || document.querySelector("#confirm-modal:not(.hidden)")) return;
179
+ if (event.target.closest && event.target.closest(".worker-runtime-options")) return;
230
180
  if (event.key === "Escape") {
231
181
  event.preventDefault();
232
182
  event.stopPropagation();
@@ -247,7 +197,14 @@ function handleDialogKeydown(event) {
247
197
  }
248
198
  }
249
199
 
250
- export function closeHomeMateSettings() {
200
+ export function closeHomeMateSettings(force) {
201
+ if (force !== true && savePending()) return false;
202
+ if (force !== true && (hasMateProfileDraft() || hasMateInstructionDraft() || hasHomeMateModelDraft())) {
203
+ var previous = document.activeElement;
204
+ showConfirm("Discard unsaved Mate settings?", function () { closeHomeMateSettings(true); }, "Discard changes", false, function () { if (previous && previous.isConnected) previous.focus(); });
205
+ document.getElementById("confirm-cancel").focus();
206
+ return false;
207
+ }
251
208
  var opener = dialogOpener;
252
209
  if (dialog) dialog.remove();
253
210
  dialog = null;
@@ -258,7 +215,12 @@ export function closeHomeMateSettings() {
258
215
  memoryState = null;
259
216
  knowledgeState = null;
260
217
  modelRequested = false;
218
+ store.set({mateSettingsAvatarOpen:false});
219
+ resetMateProfile();
220
+ resetMateColor();
261
221
  clearHomeMateModelPicker();
222
+ resetMateInstructions(null);
223
+ clearHomeMateAvatarEditor();
262
224
  document.removeEventListener("keydown", handleDialogKeydown, true);
263
225
  document.body.classList.remove("home-mate-settings-open");
264
226
  setTransientDrawerMask(false);
@@ -267,11 +229,16 @@ export function closeHomeMateSettings() {
267
229
 
268
230
  export function openHomeMateSettings(mateId, opener, options) {
269
231
  var mate = getMate(mateId);
270
- if (!mate) return false;
271
- closeHomeMateSettings();
232
+ if (!mate || (dialog && savePending())) return false;
233
+ if (dialog && (hasMateProfileDraft() || hasMateInstructionDraft() || hasHomeMateModelDraft())) {
234
+ showConfirm("Discard unsaved Mate settings?", function () { closeHomeMateSettings(true); openHomeMateSettings(mateId, opener, options); }, "Discard changes", false);
235
+ return false;
236
+ }
237
+ closeHomeMateSettings(true);
238
+ resetMateInstructions(mateId);
272
239
  dialogMateId = mateId;
273
240
  dialogOpener = opener || document.activeElement;
274
- dialogSection = options && options.section === "model" ? "model" : "general";
241
+ dialogSection = options && ["model", "prompt"].indexOf(options.section) !== -1 ? options.section : "general";
275
242
  modelRequested = false;
276
243
  resetHomeMateModelPicker(mateId, getMateName(mate), mate, options && options.sessionId);
277
244
  var overlay = document.createElement("div");
@@ -305,7 +272,7 @@ export function openHomeMateSettings(mateId, opener, options) {
305
272
  var nav = document.createElement("nav");
306
273
  nav.className = "home-mate-settings-nav";
307
274
  nav.setAttribute("aria-label", "Mate settings sections");
308
- var sections = ["general", "model", "memory", "knowledge"];
275
+ var sections = ["general", "model", "prompt"];
309
276
  for (var i = 0; i < sections.length; i++) {
310
277
  (function (section) {
311
278
  var button = document.createElement("button");
@@ -319,7 +286,7 @@ export function openHomeMateSettings(mateId, opener, options) {
319
286
  var content = document.createElement("section");
320
287
  content.className = "home-mate-settings-content";
321
288
  var contentTitle = document.createElement("h3");
322
- contentTitle.className = "home-mate-settings-content-title sr-only";
289
+ contentTitle.className = "home-mate-settings-content-title";
323
290
  contentTitle.tabIndex = -1;
324
291
  contentTitle.textContent = "Mate settings content";
325
292
  var body = document.createElement("div");
@@ -339,6 +306,7 @@ export function openHomeMateSettings(mateId, opener, options) {
339
306
  document.addEventListener("keydown", handleDialogKeydown, true);
340
307
  renderDialogContent();
341
308
  requestSection(dialogSection);
309
+ loadMateInstructions(renderDialogContent);
342
310
  refreshIcons();
343
311
  requestAnimationFrame(function () { close.focus({ preventScroll: true }); });
344
312
  return true;
@@ -361,23 +329,48 @@ export function handleHomeMateKnowledgeState(msg) {
361
329
  export function handleHomeMateModelsState(msg) {
362
330
  if (!dialog || !applyHomeMateModelsState(msg)) return false;
363
331
  if (dialogSection === "model") {
364
- var restoreFocus = restoreModelFocusAfterRender();
365
332
  renderDialogContent();
366
- restoreFocus();
367
333
  }
368
334
  return true;
369
335
  }
370
336
 
371
337
  export function handleHomeMateModelResult(msg) {
372
338
  if (!dialog || !applyHomeMateModelResult(msg)) return false;
339
+ if (msg.ok) {
340
+ var mates = store.get('cachedMatesList') || [];
341
+ store.set({ cachedMatesList: mates.map(function (mate) {
342
+ return mate && mate.id === msg.mateId ? Object.assign({}, mate, { vendor: msg.vendor, model: msg.model }) : mate;
343
+ }) });
344
+ }
373
345
  if (dialogSection === "model") {
374
- var restoreFocus = restoreModelFocusAfterRender();
375
346
  renderDialogContent();
376
- restoreFocus();
377
347
  }
378
348
  return true;
379
349
  }
380
350
 
381
- export function syncHomeMateSettingsTarget() {
382
- if (dialogMateId && !getMate(dialogMateId)) closeHomeMateSettings();
351
+ export function syncHomeMateSettingsTarget(msg) {
352
+ if (msg) { applyMateProfile(msg); applyMateColorResult(msg); }
353
+ if (!dialogMateId) return;
354
+ var mate = getMate(dialogMateId);
355
+ if (!mate) closeHomeMateSettings();
356
+ else {
357
+ confirmHomeMateAvatar(mate);
358
+ dialog.querySelector(".home-mate-settings-header span").textContent = getMateName(mate);
359
+ renderDialogContent();
360
+ }
361
+ }
362
+
363
+ export function handleHomeMateAvatarError(msg) {
364
+ if (!dialog || !msg) return false;
365
+ var colorHandled = applyMateColorResult(msg);
366
+ var profileHandled = applyMateProfile(msg);
367
+ if (!colorHandled && !profileHandled && !failHomeMateAvatar(msg.mateId, msg.error)) return false;
368
+ renderDialogContent();
369
+ return true;
370
+ }
371
+
372
+ export function handleMateInstructionsResult(msg) {
373
+ if (!dialog || !applyMateInstructions(msg)) return false;
374
+ if (dialogSection === 'prompt') renderDialogContent();
375
+ return true;
383
376
  }
@@ -1,9 +1,12 @@
1
1
  // Reversible project-chrome suppression for the home route.
2
+ import { store } from './store.js';
2
3
 
3
4
  export function showHomeShell() {
4
5
  document.body.classList.add("home-active");
6
+ store.set({ homeShellVisible: true });
5
7
  }
6
8
 
7
9
  export function hideHomeShell() {
8
10
  document.body.classList.remove("home-active");
11
+ store.set({ homeShellVisible: false });
9
12
  }
@@ -1,6 +1,5 @@
1
1
  // One-shot routing for a server-selected project or the no-project Home fallback.
2
2
  import { store } from './store.js';
3
- import { rememberHomePrimarySurface } from './home-surface.js';
4
3
 
5
4
  var initialized = false;
6
5
  var resolved = false;
@@ -17,12 +16,7 @@ export function resolveHomeBootDestination(options) {
17
16
  // Home is entered only through an explicit in-app action; reloading `/`
18
17
  // returns to the project workspace even if Home was the last open surface.
19
18
  if (options.currentSlug) return "project";
20
- if (options.surfaceLoaded !== true) return "wait";
21
19
  if (options.projectsLoaded !== true) return "wait";
22
- if (options.surface === "home") {
23
- if (options.dockLoaded !== true) return "wait";
24
- return "home";
25
- }
26
20
  return "projects";
27
21
  }
28
22
 
@@ -50,7 +44,6 @@ function settleBoot(state) {
50
44
  if (destination === "wait") return;
51
45
  resolved = true;
52
46
  if (destination === "project") {
53
- rememberHomePrimarySurface("project");
54
47
  if (!explicitProjectPath(location.pathname)) {
55
48
  var route = projectRoute(state.currentSlug);
56
49
  if (route) history.replaceState(null, "", route);
@@ -61,8 +54,7 @@ function settleBoot(state) {
61
54
  if (input && !input.disabled) input.focus({ preventScroll: true });
62
55
  return;
63
56
  }
64
- if (destination === "projects") store.set({ homeSurfaceBootResolved: true, projectsHubRestoreRequested: true });
65
- else store.set({ homeSurfaceBootResolved: true, homeSurfaceRestoreRequested: true });
57
+ store.set({ homeSurfaceBootResolved: true, projectsHubRestoreRequested: true });
66
58
  finishPendingShell();
67
59
  }
68
60
 
@@ -0,0 +1,95 @@
1
+ // Singleton tooltip controller for the compact project strip.
2
+ // Accessible trigger labels remain on the trigger; visible tooltip metadata is
3
+ // explicit so names containing commas are never parsed as status copy.
4
+
5
+ var iconStripTooltip = null;
6
+
7
+ function tooltipMetadata(value) {
8
+ if (typeof value === "string") return { text: value };
9
+ return value && typeof value === "object" ? value : { text: "" };
10
+ }
11
+
12
+ export function hideIconTooltip() {
13
+ if (!iconStripTooltip) return;
14
+ iconStripTooltip.remove();
15
+ iconStripTooltip = null;
16
+ }
17
+
18
+ export function showIconTooltip(el, text, options) {
19
+ hideIconTooltip();
20
+ var metadata = Object.assign({ text: text || "" }, options || {});
21
+ var tip = document.createElement("div");
22
+ tip.className = "icon-strip-tooltip";
23
+ tip.setAttribute("role", "tooltip");
24
+ tip.dataset.tooltipKind = metadata.kind || "project";
25
+ if (metadata.kind === "mate") {
26
+ tip.classList.add("icon-strip-tooltip-mate");
27
+ var identity = document.createElement("span");
28
+ identity.className = "icon-strip-tooltip-mate-identity";
29
+ var avatar = document.createElement("img");
30
+ avatar.className = "icon-strip-tooltip-mate-avatar";
31
+ avatar.alt = "";
32
+ avatar.draggable = false;
33
+ avatar.src = metadata.avatarUrl || "";
34
+ var label = document.createElement("span");
35
+ label.className = "icon-strip-tooltip-label";
36
+ label.textContent = metadata.text;
37
+ identity.appendChild(avatar);
38
+ identity.appendChild(label);
39
+ tip.appendChild(identity);
40
+ } else {
41
+ tip.textContent = metadata.text;
42
+ }
43
+ document.body.appendChild(tip);
44
+ iconStripTooltip = tip;
45
+
46
+ requestAnimationFrame(function () {
47
+ if (iconStripTooltip !== tip || !tip.isConnected) return;
48
+ var rect = el.getBoundingClientRect();
49
+ tip.style.top = (rect.top + rect.height / 2 - tip.offsetHeight / 2) + "px";
50
+ tip.classList.add("visible");
51
+ });
52
+ }
53
+
54
+ export function showIconTooltipHtml(el, html, options) {
55
+ hideIconTooltip();
56
+ options = options || {};
57
+ var tip = document.createElement("div");
58
+ tip.className = "icon-strip-tooltip" + (options.className ? " " + options.className : "");
59
+ tip.setAttribute("role", "tooltip");
60
+ tip.style.whiteSpace = "normal";
61
+ tip.style.maxWidth = "260px";
62
+ tip.innerHTML = html;
63
+ document.body.appendChild(tip);
64
+ iconStripTooltip = tip;
65
+
66
+ requestAnimationFrame(function () {
67
+ if (iconStripTooltip !== tip || !tip.isConnected) return;
68
+ var rect = el.getBoundingClientRect();
69
+ if (options.placement === "bottom") {
70
+ var left = rect.left + rect.width / 2 - tip.offsetWidth / 2;
71
+ left = Math.max(12, Math.min(left, window.innerWidth - tip.offsetWidth - 12));
72
+ tip.style.left = left + "px";
73
+ tip.style.top = (rect.bottom + 8) + "px";
74
+ } else {
75
+ tip.style.top = (rect.top + rect.height / 2 - tip.offsetHeight / 2) + "px";
76
+ }
77
+ tip.classList.add("visible");
78
+ });
79
+ }
80
+
81
+ export function bindIconTooltip(el, resolveMetadata) {
82
+ function show() {
83
+ var metadata = tooltipMetadata(typeof resolveMetadata === "function" ? resolveMetadata() : resolveMetadata);
84
+ showIconTooltip(el, metadata.text || "", metadata);
85
+ }
86
+ el.addEventListener("mouseenter", show);
87
+ el.addEventListener("focus", show);
88
+ el.addEventListener("mouseleave", hideIconTooltip);
89
+ el.addEventListener("blur", hideIconTooltip);
90
+ el.addEventListener("click", hideIconTooltip);
91
+ el.addEventListener("contextmenu", hideIconTooltip);
92
+ el.addEventListener("keydown", function (event) {
93
+ if (event.key === "Escape") hideIconTooltip();
94
+ });
95
+ }
@@ -0,0 +1,35 @@
1
+ export function isRecordEdit(edit) {
2
+ return !!(edit && !edit.schema && !edit.view && !edit.settings && !edit.recover);
3
+ }
4
+
5
+ export function buildRecordAttempt(edit, databaseId, databaseRevision, values) {
6
+ if (edit.attempt) return edit.attempt;
7
+ var creating = edit.creating === true;
8
+ var payload = { databaseId: databaseId, values: JSON.parse(JSON.stringify(values)), operationId: edit.operationId };
9
+ if (creating) payload.expectedDatabaseRevision = databaseRevision;
10
+ else { payload.recordId = edit.recordId; payload.expectedRevision = edit.baseVersion; }
11
+ return { type: creating ? 'knowledge_db_record_create' : 'knowledge_db_record_update', payload: payload, creating: creating, recordId: edit.recordId || null, submittedVersion: edit.version || 0 };
12
+ }
13
+
14
+ export function buildCreateAttempt(draft, name) {
15
+ if (draft.attempt) return draft.attempt;
16
+ return { operationId: draft.operationId, name: name };
17
+ }
18
+
19
+ export function isCurrentAttempt(edit, attempt) {
20
+ return !!(edit && edit.attempt && attempt && edit.attempt.payload.operationId === attempt.payload.operationId);
21
+ }
22
+
23
+ export function applyRecordSave(edit, record, newOperationId, acknowledgedAttempt) {
24
+ if (!isCurrentAttempt(edit, acknowledgedAttempt)) return { edit: edit, later: false, stale: true };
25
+ var later = (edit.version || 0) > acknowledgedAttempt.submittedVersion;
26
+ if (!later) return { edit: null, later: false };
27
+ return { later: true, edit: Object.assign({}, edit, { creating: false, recordId: record.id, baseVersion: record.revision, saving: false, retryPending: false, attempt: null, operationId: newOperationId, error: '' }) };
28
+ }
29
+
30
+ export function updateViewPresentation(view, values) {
31
+ var next = Object.assign({}, view, { name: values.name, columns: values.columns });
32
+ if (view.filters.length <= 1) next.filters = values.filters;
33
+ if (view.sorts.length <= 1) next.sorts = values.sorts;
34
+ return next;
35
+ }