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.
@@ -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>&uarr;</kbd> <kbd>&darr;</kbd> navigate</span>' +
200
+ '<span><kbd>Enter</kbd> open</span>' +
201
+ '<span><kbd>Esc</kbd> back</span>';
202
+ } else {
203
+ shortcuts =
204
+ '<span><kbd>&uarr;</kbd> <kbd>&darr;</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 sticky notes or other non-chat textareas
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 = 1;
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 === 1 ? "hidden" : "visible";
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
- collectMateWizardData();
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 > 1) {
246
- collectMateWizardData();
256
+ if (mateWizardStep > 0) {
257
+ if (mateWizardStep > 1) collectMateWizardData();
247
258
  mateWizardStep--;
248
259
  updateMateWizardStep();
249
260
  }