clay-server 2.13.0-beta.2 → 2.13.0-beta.4
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/bin/cli.js +48 -0
- package/lib/daemon.js +16 -3
- package/lib/notes.js +1 -1
- package/lib/project.js +275 -7
- package/lib/public/app.js +250 -34
- package/lib/public/css/admin.css +71 -0
- package/lib/public/css/command-palette.css +319 -0
- package/lib/public/css/icon-strip.css +19 -3
- package/lib/public/css/input.css +2 -1
- package/lib/public/css/loop.css +26 -0
- package/lib/public/css/mates.css +73 -0
- package/lib/public/css/overlays.css +3 -0
- package/lib/public/css/scheduler.css +29 -5
- package/lib/public/css/sidebar.css +28 -6
- package/lib/public/css/sticky-notes.css +61 -12
- package/lib/public/css/title-bar.css +24 -30
- package/lib/public/index.html +48 -13
- package/lib/public/modules/admin.js +109 -1
- package/lib/public/modules/command-palette.js +549 -0
- package/lib/public/modules/input.js +10 -2
- package/lib/public/modules/mate-wizard.js +17 -6
- package/lib/public/modules/scheduler.js +56 -64
- package/lib/public/modules/session-search.js +6 -2
- package/lib/public/modules/sidebar.js +128 -72
- package/lib/public/modules/sticky-notes.js +37 -0
- package/lib/public/style.css +1 -0
- package/lib/scheduler.js +4 -0
- package/lib/sdk-bridge.js +10 -8
- package/lib/server.js +334 -7
- package/lib/sessions.js +5 -0
- package/lib/users.js +64 -0
- package/lib/worktree.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import { escapeHtml } from './utils.js';
|
|
2
|
+
import { refreshIcons } from './icons.js';
|
|
3
|
+
import { openSearch as openSessionSearch } from './session-search.js';
|
|
4
|
+
|
|
5
|
+
var ctx;
|
|
6
|
+
var paletteEl = null;
|
|
7
|
+
var inputEl = null;
|
|
8
|
+
var resultsEl = null;
|
|
9
|
+
var footerEl = null;
|
|
10
|
+
var activeIndex = -1;
|
|
11
|
+
var items = [];
|
|
12
|
+
var debounceTimer = null;
|
|
13
|
+
var abortCtrl = null;
|
|
14
|
+
var pendingNav = null;
|
|
15
|
+
var mode = "home"; // "home" or "search"
|
|
16
|
+
var cachedHomeData = null;
|
|
17
|
+
var cachedVersion = null;
|
|
18
|
+
|
|
19
|
+
// --- Commands registry ---
|
|
20
|
+
var commands = [
|
|
21
|
+
{ id: "search", label: "Search sessions", desc: "Search across all projects", icon: "search", action: "enterSearch" },
|
|
22
|
+
{ id: "create-mate", label: "Create Mate", desc: "Create a new AI teammate", icon: "user-plus", action: "createMate" },
|
|
23
|
+
{ id: "settings", label: "Server settings", desc: "Configure server", icon: "settings", action: "openSettings" },
|
|
24
|
+
{ id: "home", label: "Home", desc: "Go to home hub", icon: "home", action: "showHome" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export function initCommandPalette(_ctx) {
|
|
28
|
+
ctx = _ctx;
|
|
29
|
+
buildDOM();
|
|
30
|
+
// Top bar search bar trigger
|
|
31
|
+
var hintBtn = document.getElementById("cmd-palette-btn");
|
|
32
|
+
if (hintBtn) {
|
|
33
|
+
var isMac = navigator.platform.indexOf("Mac") !== -1;
|
|
34
|
+
var kbdEl = hintBtn.querySelector(".cmd-palette-searchbar-kbd");
|
|
35
|
+
if (kbdEl) kbdEl.textContent = isMac ? "\u2318K" : "Ctrl+K";
|
|
36
|
+
hintBtn.addEventListener("click", function () {
|
|
37
|
+
if (isCommandPaletteOpen()) {
|
|
38
|
+
closeCommandPalette();
|
|
39
|
+
} else {
|
|
40
|
+
openCommandPalette();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
document.addEventListener("keydown", function (e) {
|
|
45
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
if (isCommandPaletteOpen()) {
|
|
48
|
+
closeCommandPalette();
|
|
49
|
+
} else {
|
|
50
|
+
openCommandPalette();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isCommandPaletteOpen() {
|
|
57
|
+
return paletteEl && !paletteEl.classList.contains("hidden");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function openCommandPalette() {
|
|
61
|
+
if (!paletteEl) return;
|
|
62
|
+
mode = "home";
|
|
63
|
+
paletteEl.classList.remove("hidden");
|
|
64
|
+
var searchbar = document.getElementById("cmd-palette-btn");
|
|
65
|
+
if (searchbar) searchbar.style.visibility = "hidden";
|
|
66
|
+
inputEl.value = "";
|
|
67
|
+
inputEl.placeholder = "Type a command or search...";
|
|
68
|
+
activeIndex = -1;
|
|
69
|
+
items = [];
|
|
70
|
+
resultsEl.innerHTML = '<div class="cmd-palette-loading">Loading...</div>';
|
|
71
|
+
updateFooter();
|
|
72
|
+
inputEl.focus();
|
|
73
|
+
fetchHomeData();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function closeCommandPalette() {
|
|
77
|
+
if (!paletteEl) return;
|
|
78
|
+
paletteEl.classList.add("hidden");
|
|
79
|
+
var searchbar = document.getElementById("cmd-palette-btn");
|
|
80
|
+
if (searchbar) searchbar.style.visibility = "";
|
|
81
|
+
inputEl.value = "";
|
|
82
|
+
resultsEl.innerHTML = "";
|
|
83
|
+
items = [];
|
|
84
|
+
activeIndex = -1;
|
|
85
|
+
mode = "home";
|
|
86
|
+
if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }
|
|
87
|
+
if (abortCtrl) { abortCtrl.abort(); abortCtrl = null; }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function setPaletteVersion(version) {
|
|
91
|
+
cachedVersion = version;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function handlePaletteSessionSwitch() {
|
|
95
|
+
if (!pendingNav) return;
|
|
96
|
+
var nav = pendingNav;
|
|
97
|
+
pendingNav = null;
|
|
98
|
+
if (ctx.currentSlug && ctx.currentSlug() === nav.slug) {
|
|
99
|
+
ctx.selectSession(nav.sessionId);
|
|
100
|
+
if (nav.query) {
|
|
101
|
+
setTimeout(function () { openSessionSearch(nav.query); }, 400);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function buildDOM() {
|
|
107
|
+
paletteEl = document.createElement("div");
|
|
108
|
+
paletteEl.className = "cmd-palette hidden";
|
|
109
|
+
paletteEl.innerHTML =
|
|
110
|
+
'<div class="cmd-palette-backdrop"></div>' +
|
|
111
|
+
'<div class="cmd-palette-dialog">' +
|
|
112
|
+
'<div class="cmd-palette-input-row">' +
|
|
113
|
+
'<i data-lucide="search"></i>' +
|
|
114
|
+
'<input class="cmd-palette-input" type="text" placeholder="Type a command or search..." autocomplete="off" spellcheck="false" />' +
|
|
115
|
+
'<span class="cmd-palette-kbd" id="cmd-palette-close"><i data-lucide="x"></i></span>' +
|
|
116
|
+
'</div>' +
|
|
117
|
+
'<div class="cmd-palette-results"></div>' +
|
|
118
|
+
'<div class="cmd-palette-footer"></div>' +
|
|
119
|
+
'</div>';
|
|
120
|
+
|
|
121
|
+
document.body.appendChild(paletteEl);
|
|
122
|
+
refreshIcons();
|
|
123
|
+
|
|
124
|
+
inputEl = paletteEl.querySelector(".cmd-palette-input");
|
|
125
|
+
resultsEl = paletteEl.querySelector(".cmd-palette-results");
|
|
126
|
+
footerEl = paletteEl.querySelector(".cmd-palette-footer");
|
|
127
|
+
|
|
128
|
+
paletteEl.querySelector(".cmd-palette-backdrop").addEventListener("click", function () {
|
|
129
|
+
closeCommandPalette();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
paletteEl.querySelector("#cmd-palette-close").addEventListener("click", function () {
|
|
133
|
+
closeCommandPalette();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
inputEl.addEventListener("input", function () {
|
|
137
|
+
var q = inputEl.value;
|
|
138
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
139
|
+
if (mode === "search") {
|
|
140
|
+
debounceTimer = setTimeout(function () {
|
|
141
|
+
fetchSearchResults(q.trim());
|
|
142
|
+
}, 250);
|
|
143
|
+
} else {
|
|
144
|
+
renderHome(q.trim());
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
inputEl.addEventListener("keydown", function (e) {
|
|
149
|
+
if (e.key === "Escape") {
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
if (mode === "search") {
|
|
152
|
+
// Back to home mode
|
|
153
|
+
mode = "home";
|
|
154
|
+
inputEl.value = "";
|
|
155
|
+
inputEl.placeholder = "Type a command or search...";
|
|
156
|
+
updateFooter();
|
|
157
|
+
renderHome("");
|
|
158
|
+
} else {
|
|
159
|
+
closeCommandPalette();
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (e.key === "Backspace" && mode === "search" && inputEl.value === "") {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
mode = "home";
|
|
166
|
+
inputEl.placeholder = "Type a command or search...";
|
|
167
|
+
updateFooter();
|
|
168
|
+
renderHome("");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (e.key === "ArrowDown") {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
setActive(activeIndex + 1);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (e.key === "ArrowUp") {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
setActive(activeIndex - 1);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (e.key === "Enter") {
|
|
182
|
+
e.preventDefault();
|
|
183
|
+
if (activeIndex >= 0 && activeIndex < items.length) {
|
|
184
|
+
activateItem(items[activeIndex]);
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
paletteEl.querySelector(".cmd-palette-dialog").addEventListener("click", function (e) {
|
|
191
|
+
e.stopPropagation();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function updateFooter() {
|
|
196
|
+
var shortcuts = '';
|
|
197
|
+
if (mode === "search") {
|
|
198
|
+
shortcuts =
|
|
199
|
+
'<span><kbd>↑</kbd> <kbd>↓</kbd> navigate</span>' +
|
|
200
|
+
'<span><kbd>Enter</kbd> open</span>' +
|
|
201
|
+
'<span><kbd>Esc</kbd> back</span>';
|
|
202
|
+
} else {
|
|
203
|
+
shortcuts =
|
|
204
|
+
'<span><kbd>↑</kbd> <kbd>↓</kbd> navigate</span>' +
|
|
205
|
+
'<span><kbd>Enter</kbd> select</span>';
|
|
206
|
+
}
|
|
207
|
+
var versionText = cachedVersion ? "v" + cachedVersion : "";
|
|
208
|
+
footerEl.innerHTML =
|
|
209
|
+
'<a href="https://github.com/chadbyte/clay" target="_blank" rel="noopener" class="cmd-palette-brand"><img src="favicon-banded.png" width="13" height="13" alt="">Clay ' + versionText + '</a>' +
|
|
210
|
+
'<span class="cmd-palette-footer-shortcuts">' + shortcuts + '</span>';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ==========================================
|
|
214
|
+
// HOME MODE: commands + recent sessions + projects
|
|
215
|
+
// ==========================================
|
|
216
|
+
|
|
217
|
+
function fetchHomeData() {
|
|
218
|
+
if (abortCtrl) { abortCtrl.abort(); abortCtrl = null; }
|
|
219
|
+
abortCtrl = new AbortController();
|
|
220
|
+
fetch("/api/palette/search", { signal: abortCtrl.signal })
|
|
221
|
+
.then(function (res) { return res.json(); })
|
|
222
|
+
.then(function (data) {
|
|
223
|
+
abortCtrl = null;
|
|
224
|
+
cachedHomeData = data.results || [];
|
|
225
|
+
renderHome("");
|
|
226
|
+
})
|
|
227
|
+
.catch(function (err) {
|
|
228
|
+
if (err.name === "AbortError") return;
|
|
229
|
+
abortCtrl = null;
|
|
230
|
+
cachedHomeData = [];
|
|
231
|
+
renderHome("");
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function renderHome(filter) {
|
|
236
|
+
var f = (filter || "").toLowerCase();
|
|
237
|
+
items = [];
|
|
238
|
+
var html = "";
|
|
239
|
+
var flatIndex = 0;
|
|
240
|
+
|
|
241
|
+
// --- Commands ---
|
|
242
|
+
var filteredCmds = commands.filter(function (c) {
|
|
243
|
+
if (!f) return true;
|
|
244
|
+
return c.label.toLowerCase().indexOf(f) !== -1 || c.desc.toLowerCase().indexOf(f) !== -1;
|
|
245
|
+
});
|
|
246
|
+
if (filteredCmds.length > 0) {
|
|
247
|
+
html += '<div class="cmd-palette-group-label">Commands</div>';
|
|
248
|
+
for (var i = 0; i < filteredCmds.length; i++) {
|
|
249
|
+
var cmd = filteredCmds[i];
|
|
250
|
+
items.push({ type: "command", data: cmd });
|
|
251
|
+
html += renderItem(flatIndex, '<i data-lucide="' + cmd.icon + '"></i>', cmd.label, cmd.desc, null);
|
|
252
|
+
flatIndex++;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// --- Recent sessions ---
|
|
257
|
+
var recentSessions = (cachedHomeData || []);
|
|
258
|
+
if (f) {
|
|
259
|
+
recentSessions = recentSessions.filter(function (s) {
|
|
260
|
+
return (s.sessionTitle || "").toLowerCase().indexOf(f) !== -1 ||
|
|
261
|
+
(s.projectTitle || "").toLowerCase().indexOf(f) !== -1;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
var maxRecent = f ? 10 : 5;
|
|
265
|
+
if (recentSessions.length > maxRecent) recentSessions = recentSessions.slice(0, maxRecent);
|
|
266
|
+
if (recentSessions.length > 0) {
|
|
267
|
+
html += '<div class="cmd-palette-group-label">Recent</div>';
|
|
268
|
+
for (var r = 0; r < recentSessions.length; r++) {
|
|
269
|
+
var s = recentSessions[r];
|
|
270
|
+
items.push({ type: "session", data: s });
|
|
271
|
+
var projLabel = (s.projectIcon || "") + " " + escapeHtml(s.projectTitle || s.projectSlug);
|
|
272
|
+
html += renderItem(flatIndex, s.projectIcon || '<i data-lucide="message-square"></i>', escapeHtml(s.sessionTitle || "New Session"), projLabel.trim(), null);
|
|
273
|
+
flatIndex++;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// --- Users (DM) ---
|
|
278
|
+
var allUsers = ctx.allUsers ? ctx.allUsers() : [];
|
|
279
|
+
var dmConversations = ctx.dmConversations ? ctx.dmConversations() : [];
|
|
280
|
+
var myId = ctx.myUserId ? ctx.myUserId() : null;
|
|
281
|
+
// Show recent DM conversations first, then filter all users when searching
|
|
282
|
+
var userList = [];
|
|
283
|
+
if (f) {
|
|
284
|
+
userList = allUsers.filter(function (u) {
|
|
285
|
+
if (u.id === myId) return false;
|
|
286
|
+
var name = (u.displayName || u.username || "").toLowerCase();
|
|
287
|
+
return name.indexOf(f) !== -1;
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
// Recent conversations only
|
|
291
|
+
for (var di = 0; di < dmConversations.length && di < 5; di++) {
|
|
292
|
+
var dmUserId = dmConversations[di];
|
|
293
|
+
for (var ai = 0; ai < allUsers.length; ai++) {
|
|
294
|
+
if (allUsers[ai].id === dmUserId && dmUserId !== myId) {
|
|
295
|
+
userList.push(allUsers[ai]);
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (userList.length > 0) {
|
|
302
|
+
html += '<div class="cmd-palette-group-label">' + (f ? "Users" : "Recent conversations") + '</div>';
|
|
303
|
+
for (var ui = 0; ui < userList.length; ui++) {
|
|
304
|
+
var user = userList[ui];
|
|
305
|
+
items.push({ type: "user", data: user });
|
|
306
|
+
var userName = escapeHtml(user.displayName || user.username);
|
|
307
|
+
var userSub = user.username ? "@" + escapeHtml(user.username) : "";
|
|
308
|
+
var uAvatarStyle = user.avatarStyle || "thumbs";
|
|
309
|
+
var uAvatarSeed = user.avatarSeed || user.username || user.id;
|
|
310
|
+
var uAvatarUrl = "https://api.dicebear.com/9.x/" + uAvatarStyle + "/svg?seed=" + encodeURIComponent(uAvatarSeed) + "&size=28";
|
|
311
|
+
var uAvatarHtml = '<img src="' + uAvatarUrl + '" width="28" height="28" style="border-radius:50%;" alt="">';
|
|
312
|
+
html += renderItem(flatIndex, uAvatarHtml, userName, userSub, null);
|
|
313
|
+
flatIndex++;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// --- Mates ---
|
|
318
|
+
var matesList = ctx.matesList ? ctx.matesList() : [];
|
|
319
|
+
var filteredMates = matesList.filter(function (m) {
|
|
320
|
+
if (!f) return true;
|
|
321
|
+
var mp = m.profile || {};
|
|
322
|
+
var name = (mp.displayName || m.name || "").toLowerCase();
|
|
323
|
+
return name.indexOf(f) !== -1;
|
|
324
|
+
});
|
|
325
|
+
if (filteredMates.length > 0) {
|
|
326
|
+
html += '<div class="cmd-palette-group-label">Mates</div>';
|
|
327
|
+
for (var mi = 0; mi < filteredMates.length; mi++) {
|
|
328
|
+
var mate = filteredMates[mi];
|
|
329
|
+
var mp = mate.profile || {};
|
|
330
|
+
items.push({ type: "mate", data: mate });
|
|
331
|
+
var mateName = escapeHtml(mp.displayName || mate.name || "Mate");
|
|
332
|
+
var avatarStyle = mp.avatarStyle || "bottts";
|
|
333
|
+
var avatarSeed = mp.avatarSeed || mate.id;
|
|
334
|
+
var avatarUrl = "https://api.dicebear.com/9.x/" + avatarStyle + "/svg?seed=" + encodeURIComponent(avatarSeed) + "&size=28";
|
|
335
|
+
var avatarHtml = '<img src="' + avatarUrl + '" width="28" height="28" style="border-radius:50%;" alt="">';
|
|
336
|
+
html += renderItem(flatIndex, avatarHtml, mateName, null, null);
|
|
337
|
+
flatIndex++;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// --- Projects ---
|
|
342
|
+
var projectList = ctx.projectList ? ctx.projectList() : [];
|
|
343
|
+
var filteredProjects = projectList.filter(function (p) {
|
|
344
|
+
if (p.isMate) return false;
|
|
345
|
+
if (p.isWorktree) return false;
|
|
346
|
+
if (!f) return true;
|
|
347
|
+
var name = (p.title || p.project || p.slug || "").toLowerCase();
|
|
348
|
+
return name.indexOf(f) !== -1;
|
|
349
|
+
});
|
|
350
|
+
if (filteredProjects.length > 0) {
|
|
351
|
+
html += '<div class="cmd-palette-group-label">Projects</div>';
|
|
352
|
+
for (var j = 0; j < filteredProjects.length; j++) {
|
|
353
|
+
var proj = filteredProjects[j];
|
|
354
|
+
items.push({ type: "project", data: proj });
|
|
355
|
+
var pName = escapeHtml(proj.title || proj.project || proj.slug);
|
|
356
|
+
var sessLabel = proj.sessions ? proj.sessions + " sessions" : "";
|
|
357
|
+
html += renderItem(flatIndex, proj.icon || '<i data-lucide="box"></i>', pName, sessLabel, null);
|
|
358
|
+
flatIndex++;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (items.length === 0) {
|
|
363
|
+
html = '<div class="cmd-palette-empty">No matching results</div>';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
resultsEl.innerHTML = html;
|
|
367
|
+
activeIndex = -1;
|
|
368
|
+
refreshIcons();
|
|
369
|
+
bindItemEvents();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ==========================================
|
|
373
|
+
// SEARCH MODE: cross-project session content search
|
|
374
|
+
// ==========================================
|
|
375
|
+
|
|
376
|
+
function enterSearchMode() {
|
|
377
|
+
mode = "search";
|
|
378
|
+
inputEl.value = "";
|
|
379
|
+
inputEl.placeholder = "Search session titles and content...";
|
|
380
|
+
resultsEl.innerHTML = '<div class="cmd-palette-empty">Type to search across all projects</div>';
|
|
381
|
+
items = [];
|
|
382
|
+
activeIndex = -1;
|
|
383
|
+
updateFooter();
|
|
384
|
+
inputEl.focus();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function fetchSearchResults(query) {
|
|
388
|
+
if (!query) {
|
|
389
|
+
resultsEl.innerHTML = '<div class="cmd-palette-empty">Type to search across all projects</div>';
|
|
390
|
+
items = [];
|
|
391
|
+
activeIndex = -1;
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (abortCtrl) { abortCtrl.abort(); abortCtrl = null; }
|
|
395
|
+
abortCtrl = new AbortController();
|
|
396
|
+
resultsEl.innerHTML = '<div class="cmd-palette-loading">Searching...</div>';
|
|
397
|
+
fetch("/api/palette/search?q=" + encodeURIComponent(query), { signal: abortCtrl.signal })
|
|
398
|
+
.then(function (res) { return res.json(); })
|
|
399
|
+
.then(function (data) {
|
|
400
|
+
abortCtrl = null;
|
|
401
|
+
renderSearchResults(data.results || [], query);
|
|
402
|
+
})
|
|
403
|
+
.catch(function (err) {
|
|
404
|
+
if (err.name === "AbortError") return;
|
|
405
|
+
abortCtrl = null;
|
|
406
|
+
resultsEl.innerHTML = '<div class="cmd-palette-empty">Search failed</div>';
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function renderSearchResults(results, query) {
|
|
411
|
+
items = [];
|
|
412
|
+
activeIndex = -1;
|
|
413
|
+
|
|
414
|
+
if (results.length === 0) {
|
|
415
|
+
resultsEl.innerHTML = '<div class="cmd-palette-empty">No results found</div>';
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Group by project
|
|
420
|
+
var groups = [];
|
|
421
|
+
var groupMap = {};
|
|
422
|
+
for (var i = 0; i < results.length; i++) {
|
|
423
|
+
var r = results[i];
|
|
424
|
+
if (!groupMap[r.projectSlug]) {
|
|
425
|
+
groupMap[r.projectSlug] = { slug: r.projectSlug, title: r.projectTitle, icon: r.projectIcon, items: [] };
|
|
426
|
+
groups.push(groupMap[r.projectSlug]);
|
|
427
|
+
}
|
|
428
|
+
groupMap[r.projectSlug].items.push(r);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
var html = "";
|
|
432
|
+
var flatIndex = 0;
|
|
433
|
+
for (var g = 0; g < groups.length; g++) {
|
|
434
|
+
var group = groups[g];
|
|
435
|
+
var label = escapeHtml(group.icon ? group.icon + " " + (group.title || group.slug) : (group.title || group.slug));
|
|
436
|
+
html += '<div class="cmd-palette-group-label">' + label + '</div>';
|
|
437
|
+
for (var j = 0; j < group.items.length; j++) {
|
|
438
|
+
var item = group.items[j];
|
|
439
|
+
items.push({ type: "search-result", data: item, query: query });
|
|
440
|
+
var snippet = item.snippet ? escapeHtml(item.snippet) : "";
|
|
441
|
+
html += renderItem(flatIndex, group.icon || '<i data-lucide="message-square"></i>', escapeHtml(item.sessionTitle || "New Session"), null, snippet);
|
|
442
|
+
flatIndex++;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
resultsEl.innerHTML = html;
|
|
447
|
+
refreshIcons();
|
|
448
|
+
bindItemEvents();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ==========================================
|
|
452
|
+
// Shared rendering / interaction helpers
|
|
453
|
+
// ==========================================
|
|
454
|
+
|
|
455
|
+
function renderItem(index, iconContent, title, desc, snippet) {
|
|
456
|
+
return '<div class="cmd-palette-item" data-index="' + index + '">' +
|
|
457
|
+
'<div class="cmd-palette-item-icon">' + iconContent + '</div>' +
|
|
458
|
+
'<div class="cmd-palette-item-body">' +
|
|
459
|
+
'<div class="cmd-palette-item-title">' + title + '</div>' +
|
|
460
|
+
(desc || snippet ?
|
|
461
|
+
'<div class="cmd-palette-item-meta">' +
|
|
462
|
+
(desc ? '<span class="cmd-palette-item-project">' + desc + '</span>' : '') +
|
|
463
|
+
(snippet ? '<span class="cmd-palette-item-snippet">' + snippet + '</span>' : '') +
|
|
464
|
+
'</div>'
|
|
465
|
+
: '') +
|
|
466
|
+
'</div>' +
|
|
467
|
+
'<div class="cmd-palette-item-arrow"><i data-lucide="arrow-right"></i></div>' +
|
|
468
|
+
'</div>';
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function bindItemEvents() {
|
|
472
|
+
var itemEls = resultsEl.querySelectorAll(".cmd-palette-item");
|
|
473
|
+
for (var k = 0; k < itemEls.length; k++) {
|
|
474
|
+
(function (el) {
|
|
475
|
+
el.addEventListener("click", function () {
|
|
476
|
+
var idx = parseInt(el.getAttribute("data-index"), 10);
|
|
477
|
+
if (idx >= 0 && idx < items.length) activateItem(items[idx]);
|
|
478
|
+
});
|
|
479
|
+
el.addEventListener("mouseenter", function () {
|
|
480
|
+
var idx = parseInt(el.getAttribute("data-index"), 10);
|
|
481
|
+
setActive(idx, true);
|
|
482
|
+
});
|
|
483
|
+
})(itemEls[k]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function setActive(idx, skipScroll) {
|
|
488
|
+
if (items.length === 0) return;
|
|
489
|
+
if (idx < 0) idx = items.length - 1;
|
|
490
|
+
if (idx >= items.length) idx = 0;
|
|
491
|
+
activeIndex = idx;
|
|
492
|
+
|
|
493
|
+
var els = resultsEl.querySelectorAll(".cmd-palette-item");
|
|
494
|
+
for (var i = 0; i < els.length; i++) {
|
|
495
|
+
els[i].classList.toggle("active", i === idx);
|
|
496
|
+
}
|
|
497
|
+
if (!skipScroll && els[idx]) {
|
|
498
|
+
els[idx].scrollIntoView({ block: "nearest" });
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function activateItem(entry) {
|
|
503
|
+
if (entry.type === "command") {
|
|
504
|
+
executeCommand(entry.data);
|
|
505
|
+
} else if (entry.type === "session" || entry.type === "search-result") {
|
|
506
|
+
navigateToSession(entry.data, entry.query || null);
|
|
507
|
+
} else if (entry.type === "project") {
|
|
508
|
+
closeCommandPalette();
|
|
509
|
+
ctx.switchProject(entry.data.slug);
|
|
510
|
+
} else if (entry.type === "mate" || entry.type === "user") {
|
|
511
|
+
closeCommandPalette();
|
|
512
|
+
ctx.openDm(entry.data.id);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function executeCommand(cmd) {
|
|
517
|
+
if (cmd.action === "enterSearch") {
|
|
518
|
+
enterSearchMode();
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
closeCommandPalette();
|
|
522
|
+
switch (cmd.action) {
|
|
523
|
+
case "createMate":
|
|
524
|
+
ctx.runAction("createMate");
|
|
525
|
+
break;
|
|
526
|
+
case "openSettings":
|
|
527
|
+
ctx.runAction("openSettings");
|
|
528
|
+
break;
|
|
529
|
+
case "showHome":
|
|
530
|
+
ctx.runAction("showHome");
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function navigateToSession(item, query) {
|
|
536
|
+
closeCommandPalette();
|
|
537
|
+
var slug = item.projectSlug;
|
|
538
|
+
var sessionId = item.sessionId;
|
|
539
|
+
|
|
540
|
+
if (ctx.currentSlug && ctx.currentSlug() === slug) {
|
|
541
|
+
ctx.selectSession(sessionId);
|
|
542
|
+
if (query) {
|
|
543
|
+
setTimeout(function () { openSessionSearch(query); }, 400);
|
|
544
|
+
}
|
|
545
|
+
} else {
|
|
546
|
+
pendingNav = { slug: slug, sessionId: sessionId, query: query };
|
|
547
|
+
ctx.switchProject(slug);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
@@ -119,6 +119,10 @@ export function sendMessage() {
|
|
|
119
119
|
export function autoResize() {
|
|
120
120
|
ctx.inputEl.style.height = "auto";
|
|
121
121
|
ctx.inputEl.style.height = Math.min(ctx.inputEl.scrollHeight, 120) + "px";
|
|
122
|
+
// Defensive: sync send/stop button whenever input size changes
|
|
123
|
+
if (ctx.processing && ctx.setSendBtnMode) {
|
|
124
|
+
ctx.setSendBtnMode(ctx.inputEl.value.trim() ? "send" : "stop");
|
|
125
|
+
}
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
// --- File path extraction from clipboard ---
|
|
@@ -464,6 +468,10 @@ export function handleInputSync(text) {
|
|
|
464
468
|
ctx.inputEl.value = text;
|
|
465
469
|
autoResize();
|
|
466
470
|
isRemoteInput = false;
|
|
471
|
+
// Sync send/stop button state
|
|
472
|
+
if (ctx.processing && ctx.setSendBtnMode) {
|
|
473
|
+
ctx.setSendBtnMode(text.trim() ? "send" : "stop");
|
|
474
|
+
}
|
|
467
475
|
}
|
|
468
476
|
|
|
469
477
|
function createFileInput(accept, capture, multiple) {
|
|
@@ -515,9 +523,9 @@ export function initInput(_ctx) {
|
|
|
515
523
|
|
|
516
524
|
// Paste handler
|
|
517
525
|
document.addEventListener("paste", function (e) {
|
|
518
|
-
// Don't intercept paste when typing in
|
|
526
|
+
// Don't intercept paste when typing in modals or other non-chat inputs
|
|
519
527
|
var target = e.target;
|
|
520
|
-
if (target && target.closest && target.closest(".sticky-note, #notes-archive")) return;
|
|
528
|
+
if (target && target.closest && target.closest(".sticky-note, #notes-archive, #ralph-wizard, .confirm-modal, .scheduler-detail")) return;
|
|
521
529
|
|
|
522
530
|
var cd = e.clipboardData;
|
|
523
531
|
if (!cd) return;
|
|
@@ -102,7 +102,7 @@ export function initMateWizard(sendWs, onMateCreated) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
export function openMateWizard() {
|
|
105
|
-
mateWizardStep =
|
|
105
|
+
mateWizardStep = 0;
|
|
106
106
|
mateWizardData = {
|
|
107
107
|
relationship: null,
|
|
108
108
|
activity: [],
|
|
@@ -165,6 +165,16 @@ function updateMateWizardStep() {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
var progressEl = document.querySelector("#mate-wizard .mate-wizard-progress");
|
|
169
|
+
var headerSpan = document.querySelector("#mate-wizard .mate-wizard-header > span");
|
|
170
|
+
if (mateWizardStep === 0) {
|
|
171
|
+
if (progressEl) progressEl.style.display = "none";
|
|
172
|
+
if (headerSpan) headerSpan.textContent = "";
|
|
173
|
+
} else {
|
|
174
|
+
if (progressEl) progressEl.style.display = "flex";
|
|
175
|
+
if (headerSpan) headerSpan.textContent = "Create a Mate";
|
|
176
|
+
}
|
|
177
|
+
|
|
168
178
|
var dots = document.querySelectorAll("#mate-wizard .mate-dot");
|
|
169
179
|
for (var j = 0; j < dots.length; j++) {
|
|
170
180
|
var dotStep = parseInt(dots[j].getAttribute("data-step"), 10);
|
|
@@ -175,8 +185,8 @@ function updateMateWizardStep() {
|
|
|
175
185
|
|
|
176
186
|
var backBtn = document.getElementById("mate-wizard-back");
|
|
177
187
|
var nextBtn = document.getElementById("mate-wizard-next");
|
|
178
|
-
if (backBtn) backBtn.style.visibility = mateWizardStep ===
|
|
179
|
-
if (nextBtn) nextBtn.textContent = mateWizardStep === 4 ? "Create Mate" : "Next";
|
|
188
|
+
if (backBtn) backBtn.style.visibility = mateWizardStep === 0 ? "hidden" : "visible";
|
|
189
|
+
if (nextBtn) nextBtn.textContent = mateWizardStep === 0 ? "Get Started" : mateWizardStep === 4 ? "Create Mate" : "Next";
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
function collectMateWizardData() {
|
|
@@ -214,7 +224,8 @@ function collectMateWizardData() {
|
|
|
214
224
|
}
|
|
215
225
|
|
|
216
226
|
function mateWizardNext() {
|
|
217
|
-
|
|
227
|
+
// Step 0 (intro) has no data to collect
|
|
228
|
+
if (mateWizardStep > 0) collectMateWizardData();
|
|
218
229
|
|
|
219
230
|
// Validate current step
|
|
220
231
|
if (mateWizardStep === 1) {
|
|
@@ -242,8 +253,8 @@ function mateWizardNext() {
|
|
|
242
253
|
}
|
|
243
254
|
|
|
244
255
|
function mateWizardBack() {
|
|
245
|
-
if (mateWizardStep >
|
|
246
|
-
collectMateWizardData();
|
|
256
|
+
if (mateWizardStep > 0) {
|
|
257
|
+
if (mateWizardStep > 1) collectMateWizardData();
|
|
247
258
|
mateWizardStep--;
|
|
248
259
|
updateMateWizardStep();
|
|
249
260
|
}
|