clay-server 2.11.0-beta.21 → 2.11.0-beta.23

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.
package/lib/public/app.js CHANGED
@@ -55,6 +55,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
55
55
  var dmTargetUser = null;
56
56
  var dmUnread = {}; // { otherUserId: count }
57
57
  var cachedAllUsers = [];
58
+ var cachedOnlineIds = [];
58
59
  var cachedDmFavorites = [];
59
60
  var cachedDmConversations = [];
60
61
  var dmRemovedUsers = {}; // { userId: true } - users explicitly removed from favorites
@@ -850,15 +851,19 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
850
851
  }
851
852
  // Update topbar with server-wide presence
852
853
  if (msg.serverUsers) {
854
+ cachedOnlineIds = msg.serverUsers.map(function (u) { return u.id; });
853
855
  renderTopbarPresence(msg.serverUsers);
856
+ // Re-render user strip online dots even without allUsers update
857
+ if (!msg.allUsers && cachedAllUsers.length > 0) {
858
+ renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
859
+ }
854
860
  }
855
861
  // Update user strip (DM targets) in icon strip
856
862
  if (msg.allUsers) {
857
863
  cachedAllUsers = msg.allUsers;
858
864
  if (msg.dmFavorites) cachedDmFavorites = msg.dmFavorites;
859
865
  if (msg.dmConversations) cachedDmConversations = msg.dmConversations;
860
- var onlineIds = (msg.serverUsers || []).map(function (u) { return u.id; });
861
- renderUserStrip(msg.allUsers, onlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
866
+ renderUserStrip(msg.allUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
862
867
  // Render my avatar (always present, hidden behind user-island)
863
868
  var meEl = document.getElementById("icon-strip-me");
864
869
  if (meEl && !meEl.hasChildNodes()) {
@@ -3665,11 +3670,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
3665
3670
  if (fromId && fromId !== myUserId) {
3666
3671
  dmUnread[fromId] = (dmUnread[fromId] || 0) + 1;
3667
3672
  // Re-render strip so non-favorited sender appears
3668
- var onlineIdsForDm = (cachedAllUsers || []).filter(function (u) {
3669
- var el = document.querySelector('.icon-strip-user[data-user-id="' + u.id + '"]');
3670
- return el && el.classList.contains("online");
3671
- }).map(function (u) { return u.id; });
3672
- renderUserStrip(cachedAllUsers, onlineIdsForDm, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
3673
+ renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
3673
3674
  updateDmBadge(fromId, dmUnread[fromId]);
3674
3675
  }
3675
3676
  }
@@ -3701,11 +3702,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
3701
3702
  }
3702
3703
  }
3703
3704
  cachedDmFavorites = msg.dmFavorites || [];
3704
- var onlineIds2 = (cachedAllUsers || []).filter(function (u) {
3705
- var el = document.querySelector('.icon-strip-user[data-user-id="' + u.id + '"]');
3706
- return el && el.classList.contains("online");
3707
- }).map(function (u) { return u.id; });
3708
- renderUserStrip(cachedAllUsers, onlineIds2, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
3705
+ renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
3709
3706
  break;
3710
3707
 
3711
3708
  case "daemon_config":
@@ -313,11 +313,14 @@
313
313
  min-width: unset;
314
314
  height: auto !important;
315
315
  width: auto !important;
316
+ max-width: 200px;
316
317
  }
317
318
 
318
319
  .sticky-note.minimized .sticky-note-spacer {
319
320
  color: inherit;
320
321
  opacity: 1;
322
+ overflow: hidden;
323
+ text-overflow: ellipsis;
321
324
  }
322
325
 
323
326
  .sticky-note.minimized .sticky-note-header button {
@@ -2405,7 +2405,7 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
2405
2405
  e.stopPropagation();
2406
2406
  toggleDmUserPicker(addBtn);
2407
2407
  });
2408
- addBtn.addEventListener("mouseenter", function () { showIconTooltip(addBtn, "Add user"); });
2408
+ addBtn.addEventListener("mouseenter", function () { showIconTooltip(addBtn, "Add DM favorite"); });
2409
2409
  addBtn.addEventListener("mouseleave", hideIconTooltip);
2410
2410
  container.appendChild(addBtn);
2411
2411
  refreshIcons();
@@ -260,7 +260,7 @@ function renderMiniMarkdown(text) {
260
260
 
261
261
  function syncTitle(noteEl, text) {
262
262
  var spacer = noteEl.querySelector(".sticky-note-spacer");
263
- if (spacer) spacer.textContent = getTitle(text);
263
+ if (spacer) spacer.textContent = getTitle(text) || "Untitled";
264
264
  }
265
265
 
266
266
  // --- HTML-to-Markdown reverse conversion (for contenteditable) ---
@@ -356,7 +356,7 @@ function renderNote(data) {
356
356
 
357
357
  var spacer = document.createElement("div");
358
358
  spacer.className = "sticky-note-spacer";
359
- spacer.textContent = getTitle(data.text);
359
+ spacer.textContent = getTitle(data.text) || "Untitled";
360
360
  header.appendChild(spacer);
361
361
 
362
362
  var addBtn = document.createElement("button");
@@ -644,7 +644,25 @@ function showFormatToolbar(rendered) {
644
644
  if (!sel.toString().trim()) return;
645
645
 
646
646
  var range = sel.getRangeAt(0);
647
- if (!rendered.contains(range.commonAncestorContainer)) return;
647
+ // When dragging outside the note, commonAncestorContainer may be a parent
648
+ // of rendered. Clamp the range to the rendered element so the toolbar shows.
649
+ if (!rendered.contains(range.commonAncestorContainer)) {
650
+ try {
651
+ var clampedRange = range.cloneRange();
652
+ if (range.startContainer === rendered || rendered.contains(range.startContainer)) {
653
+ clampedRange.selectNodeContents(rendered);
654
+ clampedRange.setStart(range.startContainer, range.startOffset);
655
+ } else if (range.endContainer === rendered || rendered.contains(range.endContainer)) {
656
+ clampedRange.selectNodeContents(rendered);
657
+ clampedRange.setEnd(range.endContainer, range.endOffset);
658
+ } else {
659
+ return;
660
+ }
661
+ range = clampedRange;
662
+ } catch (e) {
663
+ return;
664
+ }
665
+ }
648
666
 
649
667
  var toolbar = document.createElement("div");
650
668
  toolbar.className = "sn-format-toolbar";
package/lib/server.js CHANGED
@@ -2416,7 +2416,7 @@ function createServer(opts) {
2416
2416
  // Sends per-user filtered project lists + server-wide user list
2417
2417
  var presenceTimer = null;
2418
2418
  function broadcastPresenceChange() {
2419
- if (presenceTimer) return;
2419
+ if (presenceTimer) clearTimeout(presenceTimer);
2420
2420
  presenceTimer = setTimeout(function () {
2421
2421
  presenceTimer = null;
2422
2422
  if (!users.isMultiUser()) {
@@ -74,10 +74,12 @@ function createTerminalManager(opts) {
74
74
  var session = terminals.get(id);
75
75
  if (!session) return false;
76
76
 
77
+ // Skip scrollback replay if already subscribed (e.g. create then activate)
78
+ var alreadySubscribed = session.subscribers.has(ws);
77
79
  session.subscribers.add(ws);
78
80
 
79
- // Replay scrollback
80
- if (session.scrollback.length > 0) {
81
+ // Replay scrollback only for newly attached clients
82
+ if (!alreadySubscribed && session.scrollback.length > 0) {
81
83
  var replay = session.scrollback.join("");
82
84
  sendTo(ws, { type: "term_output", id: id, data: replay });
83
85
  }
package/lib/users.js CHANGED
@@ -548,6 +548,8 @@ function canAccessProject(userId, project) {
548
548
  // Admin always has access
549
549
  var user = findUserById(userId);
550
550
  if (user && user.role === "admin") return true;
551
+ // Owner always has access to their own project
552
+ if (project.ownerId && project.ownerId === userId) return true;
551
553
  // Private project — check allowedUsers
552
554
  var allowed = project.allowedUsers || [];
553
555
  return allowed.indexOf(userId) >= 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.11.0-beta.21",
3
+ "version": "2.11.0-beta.23",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",