clay-server 2.10.0 → 2.11.0-beta.10

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.
@@ -2,6 +2,8 @@
2
2
  import { refreshIcons } from './icons.js';
3
3
  import { showToast } from './utils.js';
4
4
  import { parseEmojis } from './markdown.js';
5
+ import { closeFileViewer } from './filebrowser.js';
6
+ import { renderModelList, renderModeList, renderEffortBar, renderThinkingBar, renderBetaCard } from './settings-defaults.js';
5
7
 
6
8
  var ctx = null;
7
9
  var panelEl = null;
@@ -13,25 +15,6 @@ var currentProject = null; // { slug, name, icon }
13
15
  // Emoji categories (reuse from sidebar)
14
16
  var EMOJI_CATEGORIES = null;
15
17
 
16
- var MODE_OPTIONS = [
17
- { value: "default", label: "Default", desc: "Claude asks for permission before running tools and editing files." },
18
- { value: "plan", label: "Plan", desc: "Claude creates a plan first and asks for approval before making changes." },
19
- { value: "acceptEdits", label: "Auto-accept edits", desc: "File edits are applied automatically. Claude still asks before running commands." },
20
- ];
21
-
22
- var EFFORT_LEVELS = [
23
- { value: "low", desc: "Quick, concise responses. Best for simple questions." },
24
- { value: "medium", desc: "Balanced responses with moderate reasoning. Good for most tasks." },
25
- { value: "high", desc: "Thorough responses with deeper analysis. Good for complex tasks." },
26
- { value: "max", desc: "Maximum reasoning depth. Best for the most difficult problems." },
27
- ];
28
-
29
- var MODEL_DESCRIPTIONS = {
30
- "default": "Automatically selects the best model for the task.",
31
- "sonnet": "Fast and capable. Great balance of speed and intelligence.",
32
- "haiku": "Fastest model. Best for quick tasks and simple questions.",
33
- "opus": "Most powerful model. Best for complex reasoning and analysis.",
34
- };
35
18
 
36
19
  // ===== Init =====
37
20
  export function initProjectSettings(appCtx, emojiCategories) {
@@ -151,6 +134,26 @@ export function initProjectSettings(appCtx, emojiCategories) {
151
134
  autoSaveSharedEnv();
152
135
  });
153
136
  }
137
+
138
+ // Owner: transfer
139
+ var transferBtn = document.getElementById("ps-transfer-btn");
140
+ if (transferBtn) {
141
+ transferBtn.addEventListener("click", function () {
142
+ showTransferForm();
143
+ });
144
+ }
145
+ var transferSave = document.getElementById("ps-transfer-save");
146
+ if (transferSave) {
147
+ transferSave.addEventListener("click", function () {
148
+ commitTransfer();
149
+ });
150
+ }
151
+ var transferCancel = document.getElementById("ps-transfer-cancel");
152
+ if (transferCancel) {
153
+ transferCancel.addEventListener("click", function () {
154
+ hideTransferForm();
155
+ });
156
+ }
154
157
  }
155
158
 
156
159
  // ===== Open / Close =====
@@ -169,6 +172,9 @@ export function openProjectSettings(slug, project) {
169
172
  // Populate profile
170
173
  populateProfile();
171
174
 
175
+ // Close file viewer if open (prevent split-screen)
176
+ closeFileViewer();
177
+
172
178
  // Show panel
173
179
  panelEl.classList.remove("hidden");
174
180
  refreshIcons();
@@ -225,6 +231,52 @@ function populateProfile() {
225
231
 
226
232
  // Icon
227
233
  updateIconPreview(currentProject ? currentProject.icon : null);
234
+
235
+ // Owner (only in multi-user mode)
236
+ var ownerField = document.getElementById("ps-owner-field");
237
+ if (ownerField) {
238
+ var ownerId = currentProject ? currentProject.projectOwnerId : null;
239
+ var isMultiUser = ctx.multiUser;
240
+ if (isMultiUser) {
241
+ ownerField.style.display = "";
242
+ var ownerNameEl = document.getElementById("ps-owner-name");
243
+ var transferBtn = document.getElementById("ps-transfer-btn");
244
+ if (transferBtn) transferBtn.style.display = "none";
245
+ // Fetch user list (only succeeds for admin)
246
+ fetch("/api/admin/users").then(function (r) {
247
+ if (!r.ok) throw new Error("not admin");
248
+ return r.json();
249
+ }).then(function (data) {
250
+ var users = data.users || [];
251
+ // Show owner name
252
+ if (ownerId) {
253
+ var owner = null;
254
+ for (var i = 0; i < users.length; i++) {
255
+ if (users[i].id === ownerId) { owner = users[i]; break; }
256
+ }
257
+ if (ownerNameEl) ownerNameEl.textContent = owner ? (owner.displayName || owner.username) : ownerId;
258
+ } else {
259
+ if (ownerNameEl) ownerNameEl.textContent = "Not set";
260
+ }
261
+ // Admin can always transfer
262
+ if (transferBtn) transferBtn.style.display = "";
263
+ }).catch(function () {
264
+ // Not admin, show owner name from limited info
265
+ if (ownerId) {
266
+ if (ownerNameEl) ownerNameEl.textContent = ownerId;
267
+ // Project owner can also transfer
268
+ if (ctx.myUserId && ctx.myUserId === ownerId && transferBtn) {
269
+ transferBtn.style.display = "";
270
+ }
271
+ } else {
272
+ if (ownerNameEl) ownerNameEl.textContent = "Not set";
273
+ }
274
+ });
275
+ hideTransferForm();
276
+ } else {
277
+ ownerField.style.display = "none";
278
+ }
279
+ }
228
280
  }
229
281
 
230
282
  function commitRename() {
@@ -248,6 +300,49 @@ function cancelRename() {
248
300
  if (renameBtn) renameBtn.classList.remove("hidden");
249
301
  }
250
302
 
303
+ // ===== Owner transfer =====
304
+ function showTransferForm() {
305
+ var form = document.getElementById("ps-transfer-form");
306
+ var btn = document.getElementById("ps-transfer-btn");
307
+ var select = document.getElementById("ps-transfer-select");
308
+ if (!form || !select) return;
309
+
310
+ // Fetch user list and populate select
311
+ select.innerHTML = '<option value="">Loading...</option>';
312
+ fetch("/api/admin/users").then(function (r) { return r.json(); }).then(function (data) {
313
+ var users = data.users || [];
314
+ select.innerHTML = "";
315
+ for (var i = 0; i < users.length; i++) {
316
+ var u = users[i];
317
+ var opt = document.createElement("option");
318
+ opt.value = u.id;
319
+ opt.textContent = (u.displayName || u.username) + (u.linuxUser ? " (" + u.linuxUser + ")" : "");
320
+ if (currentProject && u.id === currentProject.projectOwnerId) opt.selected = true;
321
+ select.appendChild(opt);
322
+ }
323
+ }).catch(function () {
324
+ select.innerHTML = '<option value="">Failed to load users</option>';
325
+ });
326
+
327
+ form.classList.remove("hidden");
328
+ if (btn) btn.classList.add("hidden");
329
+ }
330
+
331
+ function hideTransferForm() {
332
+ var form = document.getElementById("ps-transfer-form");
333
+ var btn = document.getElementById("ps-transfer-btn");
334
+ if (form) form.classList.add("hidden");
335
+ if (btn) btn.classList.remove("hidden");
336
+ }
337
+
338
+ function commitTransfer() {
339
+ var select = document.getElementById("ps-transfer-select");
340
+ var userId = select ? select.value : "";
341
+ if (!userId || !ctx.ws || !ctx.connected) return;
342
+ ctx.ws.send(JSON.stringify({ type: "transfer_project_owner", slug: currentSlug, userId: userId }));
343
+ hideTransferForm();
344
+ }
345
+
251
346
  function updateIconPreview(icon) {
252
347
  var preview = document.getElementById("ps-icon-preview");
253
348
  var removeBtn = document.getElementById("ps-icon-remove-btn");
@@ -358,158 +453,40 @@ function showPsEmojiPicker() {
358
453
  }
359
454
 
360
455
  // ===== Defaults =====
361
- function getModelDesc(model) {
362
- if (!model) return "";
363
- var lower = (model.value || model).toLowerCase();
364
- for (var key in MODEL_DESCRIPTIONS) {
365
- if (lower.indexOf(key) !== -1) return MODEL_DESCRIPTIONS[key];
366
- }
367
- return "";
368
- }
369
-
370
- function isSonnetModel(model) {
371
- if (!model) return false;
372
- return model.toLowerCase().indexOf("sonnet") !== -1;
456
+ function psSendMsg(type, data) {
457
+ var ws = ctx.ws;
458
+ if (ws && ws.readyState === 1) {
459
+ var msg = Object.assign({ type: type }, data);
460
+ ws.send(JSON.stringify(msg));
461
+ }
462
+ }
463
+
464
+ function psDefaultsOpts() {
465
+ return {
466
+ models: ctx.currentModels || [],
467
+ currentModel: ctx.currentModel || "",
468
+ currentMode: ctx.currentMode || "default",
469
+ currentEffort: ctx.currentEffort || "medium",
470
+ currentThinking: ctx.currentThinking || "adaptive",
471
+ currentThinkingBudget: ctx.currentThinkingBudget || 10000,
472
+ currentBetas: ctx.currentBetas || [],
473
+ sendMsg: psSendMsg,
474
+ modelMsgType: "set_project_default_model",
475
+ modeMsgType: "set_project_default_mode",
476
+ effortMsgType: "set_project_default_effort",
477
+ onModelSelect: function (model) {
478
+ renderBetaCard("ps", Object.assign({}, psDefaultsOpts(), { overrideModel: model }));
479
+ },
480
+ };
373
481
  }
374
482
 
375
483
  function populateDefaults() {
376
- var models = ctx.currentModels || [];
377
- var model = ctx.currentModel || "";
378
- var mode = ctx.currentMode || "default";
379
- var effort = ctx.currentEffort || "medium";
380
- var betas = ctx.currentBetas || [];
381
-
382
- // Model list
383
- var modelList = document.getElementById("ps-model-list");
384
- if (modelList) {
385
- modelList.innerHTML = "";
386
- for (var i = 0; i < models.length; i++) {
387
- (function (m) {
388
- var item = document.createElement("div");
389
- item.className = "settings-model-item" + (m.value === model ? " active" : "");
390
-
391
- var nameSpan = document.createElement("span");
392
- nameSpan.className = "settings-model-name";
393
- nameSpan.textContent = m.displayName || m.value;
394
- item.appendChild(nameSpan);
395
-
396
- var desc = getModelDesc(m.value);
397
- if (desc) {
398
- var descSpan = document.createElement("span");
399
- descSpan.className = "settings-model-desc";
400
- descSpan.textContent = desc;
401
- item.appendChild(descSpan);
402
- }
403
-
404
- item.addEventListener("click", function () {
405
- if (ctx.ws && ctx.connected) {
406
- ctx.ws.send(JSON.stringify({ type: "set_project_default_model", model: m.value }));
407
- }
408
- var items = modelList.querySelectorAll(".settings-model-item");
409
- for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
410
- item.classList.add("active");
411
- // Show/hide beta card based on Sonnet
412
- updateBetaCard("ps", m.value);
413
- });
414
- modelList.appendChild(item);
415
- })(models[i]);
416
- }
417
- }
418
-
419
- // Beta 1M toggle
420
- updateBetaCard("ps", model);
421
- var beta1m = document.getElementById("ps-beta-1m");
422
- if (beta1m) {
423
- var hasBeta = false;
424
- for (var bi = 0; bi < betas.length; bi++) {
425
- if (betas[bi].indexOf("context-1m") !== -1) { hasBeta = true; break; }
426
- }
427
- beta1m.checked = hasBeta;
428
- beta1m.onchange = function () {
429
- toggleBeta1m(this.checked);
430
- };
431
- }
432
-
433
- // Mode list
434
- var modeList = document.getElementById("ps-mode-list");
435
- if (modeList) {
436
- modeList.innerHTML = "";
437
- for (var k = 0; k < MODE_OPTIONS.length; k++) {
438
- (function (opt) {
439
- var item = document.createElement("div");
440
- item.className = "settings-model-item" + (opt.value === mode ? " active" : "");
441
-
442
- var nameSpan = document.createElement("span");
443
- nameSpan.className = "settings-model-name";
444
- nameSpan.textContent = opt.label;
445
- item.appendChild(nameSpan);
446
-
447
- var descSpan = document.createElement("span");
448
- descSpan.className = "settings-model-desc";
449
- descSpan.textContent = opt.desc;
450
- item.appendChild(descSpan);
451
-
452
- item.addEventListener("click", function () {
453
- if (ctx.ws && ctx.connected) {
454
- ctx.ws.send(JSON.stringify({ type: "set_project_default_mode", mode: opt.value }));
455
- }
456
- var items = modeList.querySelectorAll(".settings-model-item");
457
- for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
458
- item.classList.add("active");
459
- });
460
- modeList.appendChild(item);
461
- })(MODE_OPTIONS[k]);
462
- }
463
- }
464
-
465
- // Effort bar
466
- var effortBar = document.getElementById("ps-effort-bar");
467
- if (effortBar) {
468
- effortBar.innerHTML = "";
469
- for (var e = 0; e < EFFORT_LEVELS.length; e++) {
470
- (function (lvl) {
471
- var btn = document.createElement("button");
472
- btn.className = "settings-btn-option" + (lvl.value === effort ? " active" : "");
473
- btn.textContent = lvl.value.charAt(0).toUpperCase() + lvl.value.slice(1);
474
- btn.title = lvl.desc;
475
- btn.addEventListener("click", function () {
476
- if (ctx.ws && ctx.connected) {
477
- ctx.ws.send(JSON.stringify({ type: "set_project_default_effort", effort: lvl.value }));
478
- }
479
- var btns = effortBar.querySelectorAll(".settings-btn-option");
480
- for (var j = 0; j < btns.length; j++) btns[j].classList.remove("active");
481
- btn.classList.add("active");
482
- });
483
- effortBar.appendChild(btn);
484
- })(EFFORT_LEVELS[e]);
485
- }
486
- }
487
- }
488
-
489
- function updateBetaCard(prefix, model) {
490
- var card = document.getElementById(prefix + "-beta-card");
491
- if (card) {
492
- card.style.display = isSonnetModel(model) ? "" : "none";
493
- }
494
- }
495
-
496
- function toggleBeta1m(enable) {
497
- var betas = ctx.currentBetas || [];
498
- var newBetas;
499
- if (enable) {
500
- newBetas = betas.slice();
501
- newBetas.push("context-1m-2025-08-07");
502
- } else {
503
- newBetas = [];
504
- for (var i = 0; i < betas.length; i++) {
505
- if (betas[i].indexOf("context-1m") === -1) {
506
- newBetas.push(betas[i]);
507
- }
508
- }
509
- }
510
- if (ctx.ws && ctx.connected) {
511
- ctx.ws.send(JSON.stringify({ type: "set_betas", betas: newBetas }));
512
- }
484
+ var opts = psDefaultsOpts();
485
+ renderModelList("ps", opts);
486
+ renderBetaCard("ps", opts);
487
+ renderModeList("ps", opts);
488
+ renderEffortBar("ps", opts);
489
+ renderThinkingBar("ps", opts);
513
490
  }
514
491
 
515
492
  // ===== Instructions (CLAUDE.md) =====
@@ -899,3 +876,12 @@ export function updateProjectSettingsName(name) {
899
876
  var navTitle = document.getElementById("ps-nav-title");
900
877
  if (navTitle) navTitle.textContent = name || "-";
901
878
  }
879
+
880
+ export function handleProjectOwnerChanged(msg) {
881
+ if (currentProject) {
882
+ currentProject.projectOwnerId = msg.ownerId;
883
+ }
884
+ var ownerNameEl = document.getElementById("ps-owner-name");
885
+ if (ownerNameEl) ownerNameEl.textContent = msg.ownerName || msg.ownerId || "Not set";
886
+ showToast("Project ownership transferred to " + (msg.ownerName || "new owner"));
887
+ }