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
@@ -33,13 +33,14 @@ var MATE_TOOLS = [
33
33
  { id: "mate-memory-btn", icon: "brain", label: "Memory", countId: "mate-memory-count" },
34
34
  { id: "mate-knowledge-btn", icon: "book-open", label: "Knowledge", countId: "mate-knowledge-count" },
35
35
  { id: "mate-sticky-notes-btn", icon: "sticky-note", label: "Sticky Notes" },
36
- { id: "mate-debate-btn", icon: "mic", label: "Debate" },
36
+ { id: "mate-debates-btn", icon: "messages-square", label: "Debates" },
37
37
  { id: "mate-mcp-btn", icon: "cable", label: "MCP / Skills", countId: "mate-mcp-sidebar-count" },
38
38
  ];
39
39
 
40
40
  export var PALETTES = {
41
41
  session: {
42
42
  tools: SESSION_TOOLS,
43
+ fixedTools: [{ id: "you-button", icon: "user-round", label: "Me", countId: "me-unread-badge", fixed: true, scope: "clay" }],
43
44
  activeContainerId: "session-actions",
44
45
  hiddenSectionId: "session-actions-hidden",
45
46
  },
@@ -57,13 +58,13 @@ export var PALETTES = {
57
58
  //
58
59
  // Skills now shares the MCP / Skills entry; explicit MCP placement wins.
59
60
  var LEGACY_SESSION_TOOL_IDS = { "skills-btn": "mcp-btn" };
60
- var LEGACY_MATE_TOOL_IDS = { "mate-skills-btn": "mate-mcp-btn" };
61
+ var LEGACY_MATE_TOOL_IDS = { "mate-skills-btn": "mate-mcp-btn", "mate-debate-btn": "mate-debates-btn" };
61
62
 
62
63
  // Tools withdrawn from the session palette with no replacement. Git moved to
63
64
  // the always-visible repository placard below the tool strip, so it no longer
64
65
  // consumes an icon slot. A saved preference naming it is dropped rather than
65
66
  // left to linger in stored order forever.
66
- var RETIRED_SESSION_TOOL_IDS = ["git-sidebar-btn", "loop-tool-btn"];
67
+ var RETIRED_SESSION_TOOL_IDS = ["git-sidebar-btn", "loop-tool-btn", "debates-btn"];
67
68
 
68
69
  function indexOfId(list, id) {
69
70
  for (var i = 0; i < list.length; i++) {
@@ -37,6 +37,8 @@ function buildPalette(name) {
37
37
  var active = document.getElementById(palette.activeContainerId);
38
38
  if (!active) return;
39
39
 
40
+ (palette.fixedTools || []).forEach(function (tool) { active.appendChild(buildToolButton(tool, name)); });
41
+
40
42
  // Create buttons with stable IDs and append in registry order (default).
41
43
  for (var i = 0; i < palette.tools.length; i++) {
42
44
  active.appendChild(buildToolButton(palette.tools[i], name));
@@ -73,8 +75,11 @@ function buildToolButton(tool, paletteName) {
73
75
  btn.setAttribute('aria-label', tool.label);
74
76
  btn.dataset.toolId = tool.id;
75
77
  btn.dataset.palette = paletteName;
76
- // Draggable at all times so users can reorder in place.
78
+ // Ordinary tools can be reordered; fixed tools retain their first slots.
77
79
  btn.draggable = true;
80
+ if (tool.fixed) { btn.draggable = false; btn.dataset.fixed = 'true'; }
81
+ if (tool.scope) btn.dataset.scope = tool.scope;
82
+ if (tool.id === 'you-button') { btn.classList.add('you-button'); btn.setAttribute('aria-pressed', 'false'); }
78
83
 
79
84
  var icon = document.createElement('i');
80
85
  icon.setAttribute('data-lucide', tool.icon);
@@ -89,10 +94,12 @@ function buildToolButton(tool, paletteName) {
89
94
  var count = document.createElement('span');
90
95
  count.id = tool.countId;
91
96
  count.className = 'sidebar-badge hidden';
97
+ if (tool.id === 'you-button') { count.className = 'sidebar-badge me-unread-badge'; count.hidden = true; }
92
98
  btn.appendChild(count);
93
99
  }
94
100
 
95
101
  btn.addEventListener('dragstart', function (e) {
102
+ if (tool.fixed) { e.preventDefault(); return; }
96
103
  var container = btn.parentNode;
97
104
  if (!container || container.id !== PALETTES[paletteName].activeContainerId) {
98
105
  e.preventDefault();
@@ -122,6 +129,7 @@ function getDragAfterElement(container, x, y) {
122
129
  var closest = null;
123
130
  var closestDist = Number.POSITIVE_INFINITY;
124
131
  for (var i = 0; i < tiles.length; i++) {
132
+ if (tiles[i].dataset.fixed === 'true') continue;
125
133
  var rect = tiles[i].getBoundingClientRect();
126
134
  // Pointer is "before" the tile if it's left of the tile's horizontal
127
135
  // midpoint on the same row (or on a higher row).
@@ -164,7 +172,7 @@ function applyPreferences(name, prefs) {
164
172
  function place(id) {
165
173
  if (placed[id]) return;
166
174
  var btn = document.getElementById(id);
167
- if (!btn) return;
175
+ if (!btn || btn.dataset.fixed === 'true') return;
168
176
  active.appendChild(btn);
169
177
  placed[id] = true;
170
178
  }
@@ -201,7 +209,9 @@ function savePreferences(name) {
201
209
 
202
210
  var order = [];
203
211
  var activeTiles = active.querySelectorAll('[data-tool-id]');
204
- for (var i = 0; i < activeTiles.length; i++) order.push(activeTiles[i].dataset.toolId);
212
+ for (var i = 0; i < activeTiles.length; i++) {
213
+ if (activeTiles[i].dataset.fixed !== 'true') order.push(activeTiles[i].dataset.toolId);
214
+ }
205
215
 
206
216
  // Every tool is visible, so the explicit order is the whole preference.
207
217
  fetch('/api/user/tool-palettes', {
@@ -11,6 +11,7 @@ import { VENDOR_NAMES } from './app-rendering.js';
11
11
  import { startThinkingSegment, appendThinkingText, stopThinkingSegment, resetThinkingTurn, clearThinkingState, saveThinkingState, restoreThinkingState, updateThinkingTokenEstimate } from './thinking-lifecycle.js';
12
12
  import { buildElicitationCard, markElicitationCard, showElicitationError } from './elicitation-form.js';
13
13
  import { findAskUserCard, getAskUserRequestKey, hasAskUserRequest, rememberAskUserRequest, markAskUserRequestAnswered, saveAskUserState, restoreAskUserState, resetAskUserState } from './ask-user-state.js';
14
+ import { getProjectAssistantPlaceholder, resolveProjectAssistantIdentity, applyProjectAssistantIdentity } from './project-chat-identity.js';
14
15
 
15
16
  var ctx;
16
17
 
@@ -142,11 +143,11 @@ function updateToolGroupHeader(group) {
142
143
  refreshIcons();
143
144
  }
144
145
 
145
- // Show group header only when 2+ visible tools (or always in mate DM)
146
+ // Show group header only when 2+ visible tools (or always in a Mate conversation)
146
147
  var header = group.el.querySelector(".tool-group-header");
147
148
  var isMate = group.el.classList.contains("mate-tool-group");
148
149
  if (isMate) {
149
- // Mate DM: hide entire group when no tools, show collapsed when tools exist
150
+ // Mate conversation: hide entire group when no tools, show collapsed when tools exist
150
151
  if (group.toolCount === 0) {
151
152
  group.el.style.display = "none";
152
153
  } else {
@@ -248,12 +249,13 @@ export function renderAskUserQuestion(toolId, input) {
248
249
  container.dataset.askUserRequestKey = requestKey;
249
250
  rememberAskUserRequest(toolId);
250
251
 
251
- // Mate DM: wrap in avatar + content layout (same as msg-assistant)
252
+ // Mate conversation: wrap in avatar + content layout (same as msg-assistant)
252
253
  var mateContentWrap = null;
253
- if (ctx.isMateDm && ctx.isMateDm()) {
254
+ if (resolveProjectAssistantIdentity().kind === 'mate') {
254
255
  container.classList.add("mate-ask-user");
255
- var mateName = ctx.getMateName();
256
- var mateAvatar = ctx.getMateAvatarUrl();
256
+ var identity = resolveProjectAssistantIdentity();
257
+ var mateName = identity.name;
258
+ var mateAvatar = identity.avatarUrl;
257
259
 
258
260
  var avi = document.createElement("img");
259
261
  avi.className = "dm-bubble-avatar";
@@ -393,7 +395,7 @@ export function renderAskUserQuestion(toolId, input) {
393
395
  actions.appendChild(skipBtn);
394
396
  container.appendChild(actions);
395
397
 
396
- // Mate DM: move all content into the bubble content wrapper
398
+ // Mate conversation: move all content into the bubble content wrapper
397
399
  if (mateContentWrap) {
398
400
  var children = [];
399
401
  for (var ci = 0; ci < container.childNodes.length; ci++) {
@@ -420,12 +422,7 @@ export function disableMainInput() {
420
422
 
421
423
  export function enableMainInput() {
422
424
  ctx.inputEl.disabled = false;
423
- if (document.body.classList.contains("mate-dm-active") && document.body.dataset.mateName) {
424
- ctx.inputEl.placeholder = "Message " + document.body.dataset.mateName + "...";
425
- } else {
426
- var _v = store.get('currentVendor') || "claude";
427
- ctx.inputEl.placeholder = "Message " + (VENDOR_NAMES[_v] || VENDOR_NAMES.claude) + "...";
428
- }
425
+ ctx.inputEl.placeholder = getProjectAssistantPlaceholder(store.snap(), store.get('currentVendor'));
429
426
  }
430
427
 
431
428
  function submitAskUserAnswer(container, toolId, questions, answers, multiSelections) {
@@ -557,8 +554,8 @@ export function renderPermissionRequest(requestId, toolName, toolInput, decision
557
554
  return;
558
555
  }
559
556
 
560
- // Channel layout or Mate DM: conversational "Can I ...?" style
561
- if ((ctx.isMateDm && ctx.isMateDm()) || getChatLayout() === "channel") {
557
+ // Channel layout or Mate conversation: conversational "Can I ...?" style
558
+ if (resolveProjectAssistantIdentity().kind === "mate" || getChatLayout() === "channel") {
562
559
  renderConversationalPermission(requestId, toolName, toolInput, mateId, vendor);
563
560
  applyPermissionHints(pendingPermissions[requestId], hints);
564
561
  return;
@@ -824,10 +821,11 @@ function matePermissionInfo(toolName, toolInput) {
824
821
  }
825
822
 
826
823
  function resolvePermissionIdentity(mateId, vendor) {
827
- // Mate DM: use DM target mate info
828
- if (ctx.isMateDm && ctx.isMateDm()) {
829
- var name = ctx.getMateName();
830
- var avatar = ctx.getMateAvatarUrl();
824
+ // Use the Mate bound to this session, including legacy DM targets.
825
+ if (resolveProjectAssistantIdentity().kind === 'mate') {
826
+ var identity = resolveProjectAssistantIdentity();
827
+ var name = identity.name;
828
+ var avatar = identity.avatarUrl;
831
829
  // Override if specific mateId provided (e.g. @mention)
832
830
  if (mateId && ctx.getMateById) {
833
831
  var mentionMate = ctx.getMateById(mateId);
@@ -1577,7 +1575,7 @@ function updateTodoSticky() {
1577
1575
  export function startThinking() {
1578
1576
  ctx.finalizeAssistantBlock();
1579
1577
  var el = startThinkingSegment();
1580
- if (!ctx.isMateDm()) {
1578
+ if (resolveProjectAssistantIdentity().kind !== 'mate') {
1581
1579
  ctx.setActivity("thinking");
1582
1580
  }
1583
1581
  return el;
@@ -1605,7 +1603,8 @@ export function createToolItem(id, name) {
1605
1603
  toolGroupCounter++;
1606
1604
  var groupEl = document.createElement("div");
1607
1605
  groupEl.className = "tool-group";
1608
- var isMateToolGroup = ctx.isMateDm();
1606
+ var identity = resolveProjectAssistantIdentity();
1607
+ var isMateToolGroup = identity.kind === 'mate';
1609
1608
  if (isMateToolGroup) groupEl.classList.add("mate-tool-group");
1610
1609
  groupEl.dataset.groupId = "g" + toolGroupCounter;
1611
1610
 
@@ -1619,7 +1618,7 @@ export function createToolItem(id, name) {
1619
1618
  '<div class="tool-group-items"></div>';
1620
1619
 
1621
1620
  if (isMateToolGroup) {
1622
- var mateAvatar = ctx.getMateAvatarUrl();
1621
+ var mateAvatar = identity.avatarUrl;
1623
1622
  groupEl.innerHTML =
1624
1623
  '<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
1625
1624
  '<div class="dm-bubble-content">' + toolGroupInner + '</div>';
@@ -1627,6 +1626,7 @@ export function createToolItem(id, name) {
1627
1626
  groupEl.innerHTML = toolGroupInner;
1628
1627
  }
1629
1628
 
1629
+ applyProjectAssistantIdentity(groupEl, identity);
1630
1630
  groupEl.querySelector(".tool-group-header").addEventListener("click", function () {
1631
1631
  groupEl.classList.toggle("collapsed");
1632
1632
  if (currentToolGroup) currentToolGroup.userToggled = true;
@@ -0,0 +1,318 @@
1
+ import { refreshIcons } from './icons.js';
2
+ import { confirmSettingsAction as showConfirmDialog } from './server-settings-state.js';
3
+ import { store } from './store.js';
4
+ import { getWs } from './ws-ref.js';
5
+ import { showToast } from './utils.js';
6
+ import { getChatLayout, setChatLayout } from './theme.js';
7
+ import { applyTerminalFont } from './terminal-prefs.js';
8
+ import { userAvatarUrl } from './avatar.js';
9
+ import { syncCapsulePreference } from './capsule-preference.js';
10
+ import { getEmailAccountListCache } from './context-sources.js';
11
+ function applyProjectMateDmsClass(enabled) { document.body.classList.toggle('project-mate-dms-disabled', !enabled); }
12
+ export function showPinForm() {
13
+ var pinForm = document.getElementById('us-pin-form');
14
+ if (!pinForm) return;
15
+ if(!pinForm.classList.contains('hidden'))return;
16
+ pinForm.classList.remove('hidden');
17
+ var pinCurrentRow = document.getElementById('us-pin-current-row');
18
+ var pinInput = document.getElementById('us-pin-input');
19
+ var pinConfirmInput = document.getElementById('us-pin-confirm-input');
20
+ var pinCurrentInput = document.getElementById('us-pin-current-input');
21
+ var pinSave = document.getElementById('us-pin-save');
22
+ var pinMsg = document.getElementById('us-pin-msg');
23
+ var pinFormTitle = document.getElementById('us-pin-form-title');
24
+ if (pinFormTitle) {
25
+ pinFormTitle.textContent = store.get('userPinEnabled') ? 'Change PIN' : 'Set PIN';
26
+ }
27
+ if (pinCurrentRow) {
28
+ pinCurrentRow.classList.toggle('hidden', !store.get('userPinEnabled'));
29
+ }
30
+ if (pinInput) {
31
+ pinInput.value = '';
32
+ }
33
+ if (pinConfirmInput) pinConfirmInput.value = '';
34
+ if (pinCurrentInput) pinCurrentInput.value = '';
35
+ if (pinSave) pinSave.disabled = true;
36
+ if (pinSave) pinSave.textContent = 'Save';
37
+ if (pinMsg) {
38
+ pinMsg.textContent = '';
39
+ pinMsg.className = 'settings-hint settings-pin-error hidden';
40
+ }
41
+ if (store.get('userPinEnabled') && pinCurrentInput) {
42
+ pinCurrentInput.focus();
43
+ } else if (pinInput) {
44
+ pinInput.focus();
45
+ }
46
+ }
47
+
48
+ export function hidePinForm(force) {
49
+ var fields = document.querySelectorAll('#us-pin-form input');
50
+ if (!force && Array.from(fields).some(function (input) {return !!input.value;})) {
51
+ showConfirmDialog('Discard the unsaved PIN change?', 'Discard', function () {hidePinForm(true);});return;
52
+ }
53
+ var pinForm = document.getElementById('us-pin-form');
54
+ if (!pinForm) return;
55
+ pinForm.classList.add('hidden');
56
+ var pinCurrentRow = document.getElementById('us-pin-current-row');
57
+ var pinInput = document.getElementById('us-pin-input');
58
+ var pinConfirmInput = document.getElementById('us-pin-confirm-input');
59
+ var pinCurrentInput = document.getElementById('us-pin-current-input');
60
+ var pinSave = document.getElementById('us-pin-save');
61
+ var pinMsg = document.getElementById('us-pin-msg');
62
+ if (pinCurrentRow) {
63
+ pinCurrentRow.classList.remove('hidden');
64
+ }
65
+ if (pinInput) pinInput.value = '';
66
+ if (pinConfirmInput) pinConfirmInput.value = '';
67
+ if (pinCurrentInput) pinCurrentInput.value = '';
68
+ if (pinSave) pinSave.disabled = true;
69
+ if (pinSave) pinSave.textContent = 'Save';
70
+ if (pinMsg) {
71
+ pinMsg.textContent = '';
72
+ pinMsg.className = 'settings-hint settings-pin-error hidden';
73
+ }
74
+ }
75
+
76
+ // --- Account population ---
77
+
78
+ export function populateAccount() {
79
+ fetch('/api/profile').then(function (r) {
80
+ if (!r.ok) return null;
81
+ return r.json();
82
+ }).then(function (data) {
83
+ if (!data) return;
84
+ var displayName = data.name || data.displayName || data.username || 'Clay User';
85
+ var avatarImg = document.getElementById('us-account-avatar-img');
86
+ var avatarFallback = document.getElementById('us-account-avatar-fallback');
87
+ var accountName = document.getElementById('us-account-name');
88
+ var accountSubline = document.getElementById('us-account-subline');
89
+ var langSelect = document.getElementById('us-lang-select');
90
+ var pinSetBtnEl = document.getElementById('us-pin-set-btn');
91
+ var pinFormTitle = document.getElementById('us-pin-form-title');
92
+ if (accountName) {
93
+ accountName.textContent = displayName;
94
+ }
95
+ if (accountSubline) {
96
+ var parts = [];
97
+ if (data.username) parts.push('@' + data.username);
98
+ if (data.role) parts.push(data.role.charAt(0).toUpperCase() + data.role.slice(1));
99
+ accountSubline.textContent = parts.length ? parts.join(' · ') : 'Local profile';
100
+ }
101
+ store.set({userPinEnabled:!!data.pinEnabled});
102
+ if (pinSetBtnEl) {
103
+ pinSetBtnEl.textContent = store.get('userPinEnabled') ? 'Change PIN' : 'Set PIN';
104
+ }
105
+ if (pinFormTitle) {
106
+ pinFormTitle.textContent = store.get('userPinEnabled') ? 'Change PIN' : 'Set PIN';
107
+ }
108
+ if (langSelect) {
109
+ langSelect.value = store.get('speechLang') || 'en-US';
110
+ langSelect.dispatchEvent(new Event('settings-sync'));
111
+ }
112
+ if (avatarImg && avatarFallback) {
113
+ var avatarUrl = getCurrentUserAvatarSrc(data);
114
+ avatarImg.alt = displayName;
115
+ avatarImg.classList.remove('hidden');
116
+ avatarFallback.classList.remove('hidden');
117
+ avatarFallback.textContent = avatarInitials(displayName);
118
+ avatarImg.onload = function () {
119
+ avatarFallback.classList.add('hidden');
120
+ };
121
+ avatarImg.onerror = function () {
122
+ avatarFallback.classList.remove('hidden');
123
+ };
124
+ avatarImg.src = avatarUrl;
125
+ if (avatarImg.complete && avatarImg.naturalWidth > 0) {
126
+ avatarFallback.classList.add('hidden');
127
+ }
128
+ }
129
+ // Hide account section in single-user mode (no username)
130
+ var accountNav = document.getElementById('user-settings').querySelector('[data-section="us-account"]');
131
+ if (accountNav) accountNav.style.display = '';
132
+ if (pinSetBtnEl) pinSetBtnEl.disabled = !data.username;
133
+ document.getElementById('us-claude-allow-save').disabled=!data.username;
134
+ if(!data.username)document.getElementById('us-claude-allow-status').textContent='Personal command rules require a signed-in account.';
135
+ // Terminal font: seed in-memory prefs from server. Header-mounted
136
+ // controls (header-tui-font.js) listen to applyTerminalFont and
137
+ // sync their UI - we don't touch any DOM here.
138
+ if (data.terminalFont && typeof data.terminalFont === "object") {
139
+ applyTerminalFont(data.terminalFont.family, data.terminalFont.size);
140
+ }
141
+ // Auto-continue toggle
142
+ var acToggle = document.getElementById('us-auto-continue');
143
+ if (acToggle) acToggle.checked = !!data.autoContinueOnRateLimit;
144
+ // Theme picker self-syncs active selection via theme.js's applyTheme
145
+ // change callbacks (updatePickerActive). Nothing to do here.
146
+ // Layout switcher: sync from server response
147
+ // Sync mate onboarding state from server
148
+ if (data.mateOnboardingShown) {
149
+ store.set({mateOnboardingShown:true});
150
+ }
151
+ // Keep project Mate DM shortcuts available for legacy profiles too.
152
+ var matesOn = true;
153
+ var matesToggle = document.getElementById('us-mates-enabled');
154
+ if (matesToggle) matesToggle.checked = matesOn;
155
+ store.set({ projectMateDmsEnabled: matesOn });
156
+ applyProjectMateDmsClass(matesOn);
157
+ syncCapsulePreference(data.capsulesEnabled === true);
158
+ if (data.chatLayout) {
159
+ setChatLayout(data.chatLayout); // update local cache + CSS
160
+ }
161
+ var lSwitcher = document.getElementById('us-layout-switcher');
162
+ if (lSwitcher) {
163
+ var currentLayout = getChatLayout();
164
+ var lBtns = lSwitcher.querySelectorAll('.layout-option');
165
+ for (var li = 0; li < lBtns.length; li++) {
166
+ lBtns[li].classList.toggle('selected', lBtns[li].dataset.layout === currentLayout);
167
+ }
168
+ }
169
+ store.set({userSettingsProfile:data});
170
+ }).catch(function () {showToast('Could not load your settings. Close and reopen to retry.');});
171
+ }
172
+
173
+ function avatarInitials(value) {
174
+ var text = (value || '').trim();
175
+ if (!text) return '?';
176
+ var parts = text.split(/\s+/);
177
+ var first = parts[0].charAt(0);
178
+ var second = parts.length > 1 ? parts[1].charAt(0) : parts[0].charAt(1);
179
+ var out = first + (second || '');
180
+ return out.toUpperCase();
181
+ }
182
+
183
+ function getCurrentUserAvatarSrc(data) {
184
+ var islandAvatar = document.querySelector('.user-island-avatar img');
185
+ if (islandAvatar && islandAvatar.src) return islandAvatar.src;
186
+ if (data && data.avatarCustom) return data.avatarCustom;
187
+ return userAvatarUrl(data, 96);
188
+ }
189
+
190
+ export function savePin(pin) {
191
+ var pinInput = document.getElementById('us-pin-input');
192
+ var pinConfirmInput = document.getElementById('us-pin-confirm-input');
193
+ var pinCurrentInput = document.getElementById('us-pin-current-input');
194
+ var pinSave = document.getElementById('us-pin-save');
195
+ var pinMsg = document.getElementById('us-pin-msg');
196
+ var currentPin = pinCurrentInput ? pinCurrentInput.value.trim() : '';
197
+ var confirmPin = pinConfirmInput ? pinConfirmInput.value.trim() : '';
198
+ var newPin = pinInput ? pinInput.value.trim() : '';
199
+
200
+ if (!/^\d{6}$/.test(newPin)) {
201
+ if (pinMsg) {
202
+ pinMsg.textContent = 'PIN must be exactly 6 digits.';
203
+ pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
204
+ pinMsg.classList.remove('hidden');
205
+ }
206
+ return;
207
+ }
208
+ if (confirmPin !== newPin) {
209
+ if (pinMsg) {
210
+ pinMsg.textContent = 'PINs do not match.';
211
+ pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
212
+ pinMsg.classList.remove('hidden');
213
+ }
214
+ return;
215
+ }
216
+ if (store.get('userPinEnabled') && !/^\d{6}$/.test(currentPin)) {
217
+ if (pinMsg) {
218
+ pinMsg.textContent = 'Current PIN is required.';
219
+ pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
220
+ pinMsg.classList.remove('hidden');
221
+ }
222
+ return;
223
+ }
224
+
225
+ pinSave.disabled = true;
226
+ pinSave.textContent = 'Saving\u2026';
227
+
228
+ fetch('/api/user/pin', {
229
+ method: 'PUT',
230
+ headers: { 'Content-Type': 'application/json' },
231
+ body: JSON.stringify({ currentPin: currentPin, newPin: newPin }),
232
+ }).then(function (r) { return r.json(); }).then(function (data) {
233
+ if (data.ok) {
234
+ showToast('PIN changed');
235
+ hidePinForm(true);
236
+ store.set({userPinEnabled:true});
237
+ } else {
238
+ pinSave.disabled = false;
239
+ pinSave.textContent = 'Save';
240
+ if (pinMsg) {
241
+ pinMsg.textContent = data.error || 'Could not change your PIN. Please try again.';
242
+ pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
243
+ pinMsg.classList.remove('hidden');
244
+ }
245
+ }
246
+ }).catch(function () {
247
+ pinSave.disabled = false;
248
+ pinSave.textContent = 'Save';
249
+ if (pinMsg) {
250
+ pinMsg.textContent = 'Connection lost. Check your network and try again.';
251
+ pinMsg.className = 'settings-hint settings-pin-error us-pin-msg-err';
252
+ pinMsg.classList.remove('hidden');
253
+ }
254
+ });
255
+ }
256
+
257
+ // --- Email settings ---
258
+
259
+ export function renderEmailSettings() {
260
+ var listEl = document.getElementById('us-email-list');
261
+ if (!listEl) return;
262
+ listEl.innerHTML = '';
263
+
264
+ var accounts = getEmailAccountListCache();
265
+ if (!accounts || accounts.length === 0) {
266
+ return;
267
+ }
268
+
269
+ for (var i = 0; i < accounts.length; i++) {
270
+ var acc = accounts[i];
271
+ var row = document.createElement('div');
272
+ row.className = 'us-integration-email-row';
273
+ row.innerHTML =
274
+ '<div class="us-voice-row-controls"><div class="us-integration-brand">' + providerIcon(acc.provider) + '</div>' +
275
+ '<div class="us-voice-provider-copy"><strong>' + escHtml(acc.email) + '</strong><small>Email · ' + escHtml(({gmail:'Gmail',outlook:'Outlook Mail',yahoo:'Yahoo Mail',custom:'Custom email'})[acc.provider] || 'Custom email') + (acc.label ? ' · ' + escHtml(acc.label) : '') + '</small></div>' +
276
+ '<span class="us-integration-state">Account saved</span><button type="button" class="settings-btn-sm" aria-expanded="false">Manage</button></div>' +
277
+ '<div class="us-email-manage" hidden><p class="settings-hint">This personal mailbox can be used to read, search and send email. Disconnecting keeps your mail at the provider.</p><button type="button" class="settings-btn-sm" data-account-id="' + escHtml(acc.id) + '">Disconnect</button></div>';
278
+ var manageBtn = row.querySelector('[aria-expanded]');
279
+ manageBtn.setAttribute('aria-label', 'Manage ' + acc.email);
280
+ manageBtn.addEventListener('click', function () {
281
+ var panel = this.closest('.us-integration-email-row').querySelector('.us-email-manage');
282
+ panel.hidden = !panel.hidden; this.setAttribute('aria-expanded', String(!panel.hidden));
283
+ });
284
+ var removeBtn = row.querySelector('[data-account-id]');
285
+ removeBtn.addEventListener('click', function () {
286
+ var accountId = this.getAttribute('data-account-id');
287
+ showConfirmDialog('Disconnect this personal mailbox from Clay? Existing mail at the provider is kept.', 'Disconnect', function () {
288
+ var ws=getWs();
289
+ if(ws && ws.readyState===1)ws.send(JSON.stringify({type:'email_account_remove',accountId:accountId}));
290
+ else showToast('Disconnected from Clay. Reconnect and retry.');
291
+ });
292
+ });
293
+
294
+ listEl.appendChild(row);
295
+ }
296
+ refreshIcons();
297
+ }
298
+
299
+
300
+ var PROVIDER_ICON_PATHS = {
301
+ gmail: '/icons/email/gmail.svg',
302
+ outlook: '/icons/email/outlook.svg',
303
+ yahoo: '/icons/email/yahoo.svg',
304
+ };
305
+
306
+ function providerIcon(provider) {
307
+ var src = PROVIDER_ICON_PATHS[provider];
308
+ if (src) {
309
+ return '<img src="' + src + '" class="us-email-provider-icon" alt="' + provider + '">';
310
+ }
311
+ return '<i data-lucide="mail" class="us-email-provider-icon-fallback"></i>';
312
+ }
313
+
314
+ function escHtml(str) {
315
+ var d = document.createElement('div');
316
+ d.textContent = str;
317
+ return d.innerHTML;
318
+ }
@@ -0,0 +1,55 @@
1
+ import { store } from './store.js';
2
+ import { initPersonalThemes } from './user-settings-themes.js';
3
+ import { TERMINAL_FONT_OPTIONS, getTerminalFontFamily, getTerminalFontSize, saveTerminalFont, onTerminalFontChange } from './terminal-prefs.js';
4
+ import { enhanceSettingsSelects } from './settings-select.js';
5
+
6
+ export function initPersonalAppearance() {
7
+ var theme = document.getElementById('us-theme-picker').closest('.settings-card');
8
+ initPersonalThemes(theme);
9
+
10
+ var layout = document.getElementById('us-layout-switcher');
11
+ layout.closest('.settings-card').classList.add('us-layout-group');
12
+ layout.setAttribute('role', 'group'); layout.setAttribute('aria-label', 'Conversation layout');
13
+ layout.querySelectorAll('button').forEach(function (button) {
14
+ var bubble = button.dataset.layout === 'bubble';
15
+ button.querySelector('.layout-option-desc').textContent = bubble ? 'Centered messages in separate bubbles.' : 'Full-width conversation with avatars.';
16
+ var preview = document.createElement('span'); preview.className = 'us-layout-preview us-layout-preview-' + button.dataset.layout; preview.setAttribute('aria-hidden', 'true');
17
+ preview.innerHTML = '<i></i><i></i><i></i>'; button.prepend(preview);
18
+ });
19
+ layout.querySelectorAll('button').forEach(function (button) { button.setAttribute('aria-pressed', button.classList.contains('selected')); });
20
+ addTerminalTypography(theme.closest('.us-section'));
21
+ enhanceSettingsSelects(theme);
22
+ }
23
+
24
+ function addTerminalTypography(section) {
25
+ var content = document.createElement('section'); content.className = 'us-typography settings-card';
26
+ content.innerHTML = '<h3>Terminal typography</h3><p class="settings-hint">Used by existing and new terminals. Also available from the terminal toolbar.</p><div class="us-font-controls"><div><label class="settings-label" for="us-terminal-family">Font</label><select id="us-terminal-family" class="settings-select"></select></div><div><label class="settings-label" for="us-terminal-size">Size</label><div class="us-font-size"><input id="us-terminal-size" class="us-text-input" type="number" min="9" max="32" step="1" aria-label="Terminal font size"><span>px</span></div></div></div><div class="us-font-preview" aria-hidden="true">Aa Bb 0123 · ~/projects/clay</div><p class="settings-status" role="status" id="us-terminal-status"></p><button type="button" class="settings-btn-sm" id="us-terminal-retry" hidden>Retry save</button>';
27
+ section.appendChild(content);
28
+ var family = content.querySelector('select'); var size = content.querySelector('input');
29
+ var preview = content.querySelector('.us-font-preview'); var status = content.querySelector('[role=status]'); var retry = content.querySelector('#us-terminal-retry');
30
+ TERMINAL_FONT_OPTIONS.forEach(function (font) {
31
+ var option = document.createElement('option'); option.value = font.family; option.textContent = font.label; family.appendChild(option);
32
+ });
33
+ function sync() {
34
+ if (store.get('userTerminalDirty')) return;
35
+ var value = getTerminalFontFamily();
36
+ if (!Array.from(family.options).some(function (option) { return option.value === value; })) {
37
+ var option = document.createElement('option'); option.value = value; option.textContent = 'Custom font'; family.appendChild(option);
38
+ }
39
+ family.value = value; size.value = getTerminalFontSize();
40
+ preview.style.fontFamily = value; preview.style.fontSize = size.value + 'px';
41
+ family.dispatchEvent(new Event('settings-sync'));
42
+ }
43
+ async function save() {
44
+ var value = Number(size.value); store.set({ userTerminalDirty: true });
45
+ if (!Number.isInteger(value) || value < 9 || value > 32) { status.textContent = 'Choose a whole size from 9 to 32.'; status.dataset.state = 'error'; return; }
46
+ family.disabled = true; size.disabled = true; retry.hidden = true; status.textContent = 'Saving…'; status.dataset.state = '';
47
+ try {
48
+ await saveTerminalFont(family.value, value); store.set({ userTerminalDirty: false }); sync(); status.textContent = 'Saved.';
49
+ } catch (error) { status.textContent = error.message; status.dataset.state = 'error'; retry.hidden = false; }
50
+ finally { family.disabled = false; size.disabled = false; family.dispatchEvent(new Event('settings-sync')); }
51
+ }
52
+ family.onchange = save; size.onchange = save; retry.onclick = save;
53
+ size.oninput = function () { store.set({ userTerminalDirty: true }); };
54
+ onTerminalFontChange(sync); sync(); enhanceSettingsSelects(content);
55
+ }
@@ -0,0 +1,24 @@
1
+ import { store } from './store.js';
2
+ import { confirmSettingsAction as showConfirmDialog } from './server-settings-state.js';
3
+
4
+ function wrapEditor(content, title, summary) {
5
+ var group=document.createElement('section');group.className='us-config-group';
6
+ content.before(group);
7
+ var heading=document.createElement('div');heading.className='us-editor-heading';
8
+ var label=document.createElement('h3');label.textContent=title;
9
+ var edit=document.createElement('button');edit.type='button';edit.className='settings-btn-sm';edit.textContent='Edit';
10
+ var state=document.createElement('p');state.className='us-editor-summary';state.textContent=summary();
11
+ heading.append(label,edit);group.append(heading,state);
12
+ var help=content.querySelector('.settings-hint');var copy=null;if(help && title!=='Personal allow rules'){copy=help.cloneNode(true);group.appendChild(copy);}
13
+ group.appendChild(content);content.hidden=true;
14
+ store.subscribe(function(next,previous){if(next.userSettingsProfile!==previous.userSettingsProfile)state.textContent=summary();});
15
+ var done=document.createElement('button');done.type='button';done.className='settings-btn-sm';done.textContent='Done';content.appendChild(done);
16
+ edit.onclick=function(){if(copy)copy.hidden=true;content.hidden=false;state.hidden=true;edit.hidden=true;var first=content.querySelector('button,input,textarea,select');if(first)first.focus();};
17
+ done.onclick=function(){if(copy)copy.hidden=false;content.hidden=true;state.hidden=false;edit.hidden=false;state.textContent=summary();edit.focus();};
18
+ return {group:group,state:state,edit:edit,done:done,content:content};
19
+ }
20
+ export function initPersonalEditors() {
21
+ var allow=document.getElementById('us-claude-allow-list');var permission=wrapEditor(allow.closest('.settings-card'),'Personal allow rules',function(){var count=(store.get('userAllowSaved') || []).length;return count+' personal rules. Managed defaults apply separately.';});
22
+ permission.done.textContent='Cancel';permission.done.onclick=function(){function cancel(){allow.value=(store.get('userAllowSaved') || []).join('\n');store.set({userAllowDirty:false});permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}if(store.get('userAllowDirty'))showConfirmDialog('Discard unsaved command rules?', 'Discard',cancel);else cancel();};
23
+ store.subscribe(function(state,previous){if(state.userAllowSaved!==previous.userAllowSaved){permission.state.textContent=(state.userAllowSaved || []).length+' personal rules. Managed defaults apply separately.';if(previous.userAllowDirty && !state.userAllowDirty){permission.content.hidden=true;permission.state.hidden=false;permission.edit.hidden=false;permission.edit.focus();}}});
24
+ }