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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
2419
|
+
if (presenceTimer) clearTimeout(presenceTimer);
|
|
2420
2420
|
presenceTimer = setTimeout(function () {
|
|
2421
2421
|
presenceTimer = null;
|
|
2422
2422
|
if (!users.isMultiUser()) {
|
package/lib/terminal-manager.js
CHANGED
|
@@ -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;
|