clay-server 2.27.0-beta.8 → 2.27.0
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/README.md +10 -0
- package/lib/daemon-projects.js +164 -0
- package/lib/daemon.js +13 -126
- package/lib/mates-identity.js +132 -0
- package/lib/mates-knowledge.js +113 -0
- package/lib/mates-prompts.js +398 -0
- package/lib/mates.js +40 -599
- package/lib/project-connection.js +2 -0
- package/lib/project-debate.js +19 -12
- package/lib/project-http.js +4 -2
- package/lib/project-loop.js +110 -48
- package/lib/project-mate-interaction.js +4 -0
- package/lib/project-notifications.js +210 -0
- package/lib/project-sessions.js +5 -2
- package/lib/project-user-message.js +2 -1
- package/lib/project.js +26 -2
- package/lib/public/app.js +1193 -8521
- package/lib/public/css/command-palette.css +14 -0
- package/lib/public/css/loop.css +301 -0
- package/lib/public/css/notifications-center.css +190 -0
- package/lib/public/css/rewind.css +6 -0
- package/lib/public/index.html +89 -35
- package/lib/public/modules/app-connection.js +160 -0
- package/lib/public/modules/app-cursors.js +473 -0
- package/lib/public/modules/app-debate-ui.js +389 -0
- package/lib/public/modules/app-dm.js +627 -0
- package/lib/public/modules/app-favicon.js +212 -0
- package/lib/public/modules/app-header.js +229 -0
- package/lib/public/modules/app-home-hub.js +600 -0
- package/lib/public/modules/app-loop-ui.js +589 -0
- package/lib/public/modules/app-loop-wizard.js +439 -0
- package/lib/public/modules/app-messages.js +1560 -0
- package/lib/public/modules/app-misc.js +299 -0
- package/lib/public/modules/app-notifications.js +372 -0
- package/lib/public/modules/app-panels.js +888 -0
- package/lib/public/modules/app-projects.js +798 -0
- package/lib/public/modules/app-rate-limit.js +451 -0
- package/lib/public/modules/app-rendering.js +597 -0
- package/lib/public/modules/app-skills-install.js +234 -0
- package/lib/public/modules/command-palette.js +27 -4
- package/lib/public/modules/input.js +31 -20
- package/lib/public/modules/scheduler-config.js +1532 -0
- package/lib/public/modules/scheduler-history.js +79 -0
- package/lib/public/modules/scheduler.js +33 -1554
- package/lib/public/modules/session-search.js +13 -1
- package/lib/public/modules/sidebar-mates.js +812 -0
- package/lib/public/modules/sidebar-mobile.js +1269 -0
- package/lib/public/modules/sidebar-projects.js +1449 -0
- package/lib/public/modules/sidebar-sessions.js +986 -0
- package/lib/public/modules/sidebar.js +232 -4591
- package/lib/public/modules/store.js +27 -0
- package/lib/public/modules/ws-ref.js +7 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +96 -717
- package/lib/sdk-message-processor.js +587 -0
- package/lib/sdk-message-queue.js +42 -0
- package/lib/sdk-skill-discovery.js +131 -0
- package/lib/server-admin.js +712 -0
- package/lib/server-auth.js +737 -0
- package/lib/server-dm.js +221 -0
- package/lib/server-mates.js +281 -0
- package/lib/server-palette.js +110 -0
- package/lib/server-settings.js +479 -0
- package/lib/server-skills.js +280 -0
- package/lib/server.js +246 -2755
- package/lib/sessions.js +11 -4
- package/lib/users-auth.js +146 -0
- package/lib/users-permissions.js +118 -0
- package/lib/users-preferences.js +210 -0
- package/lib/users.js +48 -398
- package/lib/ws-schema.js +498 -0
- package/package.json +1 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// app-skills-install.js - Skill install dialog, requireSkills, requireClayMateInterview
|
|
2
|
+
// Extracted from app.js (PR-33)
|
|
3
|
+
|
|
4
|
+
import { refreshIcons, iconHtml } from './icons.js';
|
|
5
|
+
import { escapeHtml } from './utils.js';
|
|
6
|
+
import { store } from './store.js';
|
|
7
|
+
|
|
8
|
+
// --- Module-owned state (not in store) ---
|
|
9
|
+
var skillInstallModal = null;
|
|
10
|
+
var skillInstallTitle = null;
|
|
11
|
+
var skillInstallReason = null;
|
|
12
|
+
var skillInstallList = null;
|
|
13
|
+
var skillInstallOk = null;
|
|
14
|
+
var skillInstallCancel = null;
|
|
15
|
+
var skillInstallStatus = null;
|
|
16
|
+
|
|
17
|
+
var pendingSkillInstalls = [];
|
|
18
|
+
var skillInstallCallback = null;
|
|
19
|
+
var skillInstalling = false;
|
|
20
|
+
var skillInstallDone = false;
|
|
21
|
+
|
|
22
|
+
export function initSkillInstall() {
|
|
23
|
+
skillInstallModal = document.getElementById("skill-install-modal");
|
|
24
|
+
skillInstallTitle = document.getElementById("skill-install-title");
|
|
25
|
+
skillInstallReason = document.getElementById("skill-install-reason");
|
|
26
|
+
skillInstallList = document.getElementById("skill-install-list");
|
|
27
|
+
skillInstallOk = document.getElementById("skill-install-ok");
|
|
28
|
+
skillInstallCancel = document.getElementById("skill-install-cancel");
|
|
29
|
+
skillInstallStatus = document.getElementById("skill-install-status");
|
|
30
|
+
|
|
31
|
+
skillInstallCancel.addEventListener("click", hideSkillInstallModal);
|
|
32
|
+
skillInstallModal.querySelector(".confirm-backdrop").addEventListener("click", hideSkillInstallModal);
|
|
33
|
+
|
|
34
|
+
skillInstallOk.addEventListener("click", function () {
|
|
35
|
+
if (skillInstallDone) {
|
|
36
|
+
var proceedCb = skillInstallCallback;
|
|
37
|
+
skillInstallCallback = null;
|
|
38
|
+
hideSkillInstallModal();
|
|
39
|
+
if (proceedCb) proceedCb();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (skillInstalling) return;
|
|
43
|
+
skillInstalling = true;
|
|
44
|
+
skillInstallOk.disabled = true;
|
|
45
|
+
skillInstallOk.textContent = "Installing...";
|
|
46
|
+
|
|
47
|
+
var total = 0;
|
|
48
|
+
for (var i = 0; i < pendingSkillInstalls.length; i++) {
|
|
49
|
+
if (!pendingSkillInstalls[i].installed) total++;
|
|
50
|
+
}
|
|
51
|
+
skillInstallStatus.classList.remove("hidden");
|
|
52
|
+
updateSkillInstallProgress(0, total);
|
|
53
|
+
|
|
54
|
+
for (var j = 0; j < pendingSkillInstalls.length; j++) {
|
|
55
|
+
var s = pendingSkillInstalls[j];
|
|
56
|
+
if (s.installed) continue;
|
|
57
|
+
fetch(store.getState().basePath + "api/install-skill", {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "Content-Type": "application/json" },
|
|
60
|
+
body: JSON.stringify({ url: s.url, skill: s.name, scope: s.scope || "global" }),
|
|
61
|
+
}).catch(function () {});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- Functions ---
|
|
67
|
+
|
|
68
|
+
function renderSkillInstallDialog(opts, missing) {
|
|
69
|
+
var hasOutdated = false;
|
|
70
|
+
var hasMissing = false;
|
|
71
|
+
for (var c = 0; c < missing.length; c++) {
|
|
72
|
+
if (missing[c].status === "outdated") hasOutdated = true;
|
|
73
|
+
else hasMissing = true;
|
|
74
|
+
}
|
|
75
|
+
var defaultTitle = hasMissing ? "Skill Installation Required" : "Skill Update Available";
|
|
76
|
+
var defaultReason = hasMissing
|
|
77
|
+
? "This feature requires the following skill(s) to be installed."
|
|
78
|
+
: "Newer versions of the following skill(s) are available.";
|
|
79
|
+
if (hasMissing && hasOutdated) {
|
|
80
|
+
defaultTitle = "Skill Installation / Update Required";
|
|
81
|
+
defaultReason = "Some skills need to be installed or updated.";
|
|
82
|
+
}
|
|
83
|
+
skillInstallTitle.textContent = opts.title || defaultTitle;
|
|
84
|
+
skillInstallReason.textContent = opts.reason || defaultReason;
|
|
85
|
+
skillInstallList.innerHTML = "";
|
|
86
|
+
for (var i = 0; i < missing.length; i++) {
|
|
87
|
+
var s = missing[i];
|
|
88
|
+
var badge = s.status === "outdated"
|
|
89
|
+
? '<span class="skill-badge skill-badge-update">Update ' + escapeHtml(s.installedVersion || "") + ' \u2192 ' + escapeHtml(s.remoteVersion || "") + '</span>'
|
|
90
|
+
: '<span class="skill-badge skill-badge-new">New</span>';
|
|
91
|
+
var item = document.createElement("div");
|
|
92
|
+
item.className = "skill-install-item";
|
|
93
|
+
item.setAttribute("data-skill", s.name);
|
|
94
|
+
item.innerHTML = '<span class="skill-icon">🧩</span>' +
|
|
95
|
+
'<div class="skill-info">' +
|
|
96
|
+
'<span class="skill-name">' + escapeHtml(s.name) + '</span>' +
|
|
97
|
+
badge +
|
|
98
|
+
'</div>' +
|
|
99
|
+
'<span class="skill-status"></span>';
|
|
100
|
+
skillInstallList.appendChild(item);
|
|
101
|
+
}
|
|
102
|
+
skillInstallStatus.classList.add("hidden");
|
|
103
|
+
skillInstallStatus.innerHTML = "";
|
|
104
|
+
skillInstallOk.disabled = false;
|
|
105
|
+
var btnLabel = hasMissing ? "Install" : "Update";
|
|
106
|
+
if (hasMissing && hasOutdated) btnLabel = "Install / Update";
|
|
107
|
+
skillInstallOk.textContent = btnLabel;
|
|
108
|
+
skillInstallOk.className = "confirm-btn confirm-delete";
|
|
109
|
+
skillInstallModal.classList.remove("hidden");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function hideSkillInstallModal() {
|
|
113
|
+
skillInstallModal.classList.add("hidden");
|
|
114
|
+
skillInstallCallback = null;
|
|
115
|
+
pendingSkillInstalls = [];
|
|
116
|
+
skillInstalling = false;
|
|
117
|
+
skillInstallDone = false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function updateSkillInstallProgress(done, total) {
|
|
121
|
+
var hasUpdates = false;
|
|
122
|
+
for (var u = 0; u < pendingSkillInstalls.length; u++) {
|
|
123
|
+
if (pendingSkillInstalls[u].status === "outdated") { hasUpdates = true; break; }
|
|
124
|
+
}
|
|
125
|
+
var label = hasUpdates ? "Updating" : "Installing";
|
|
126
|
+
skillInstallStatus.innerHTML = '<div class="skills-spinner small"></div> ' + label + ' skills... (' + done + '/' + total + ')';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function updateSkillListItems() {
|
|
130
|
+
var items = skillInstallList.querySelectorAll(".skill-install-item");
|
|
131
|
+
for (var i = 0; i < items.length; i++) {
|
|
132
|
+
var name = items[i].getAttribute("data-skill");
|
|
133
|
+
for (var j = 0; j < pendingSkillInstalls.length; j++) {
|
|
134
|
+
if (pendingSkillInstalls[j].name === name) {
|
|
135
|
+
var statusEl = items[i].querySelector(".skill-status");
|
|
136
|
+
if (pendingSkillInstalls[j].installed) {
|
|
137
|
+
if (statusEl) {
|
|
138
|
+
statusEl.innerHTML = '<span class="skill-status-ok">' + iconHtml("circle-check") + '</span>';
|
|
139
|
+
refreshIcons();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function handleSkillInstallWs(msg) {
|
|
149
|
+
if (!skillInstalling || pendingSkillInstalls.length === 0) return;
|
|
150
|
+
for (var i = 0; i < pendingSkillInstalls.length; i++) {
|
|
151
|
+
if (pendingSkillInstalls[i].name === msg.skill) {
|
|
152
|
+
if (msg.success) {
|
|
153
|
+
pendingSkillInstalls[i].installed = true;
|
|
154
|
+
var _kis = Object.assign({}, store.getState().knownInstalledSkills);
|
|
155
|
+
_kis[msg.skill] = true;
|
|
156
|
+
store.setState({ knownInstalledSkills: _kis });
|
|
157
|
+
} else {
|
|
158
|
+
skillInstalling = false;
|
|
159
|
+
skillInstallOk.disabled = false;
|
|
160
|
+
skillInstallOk.textContent = "Install";
|
|
161
|
+
skillInstallStatus.innerHTML = "Failed to install " + escapeHtml(msg.skill) + ". Try again.";
|
|
162
|
+
updateSkillListItems();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var doneCount = 0;
|
|
169
|
+
var totalCount = pendingSkillInstalls.length;
|
|
170
|
+
for (var k = 0; k < pendingSkillInstalls.length; k++) {
|
|
171
|
+
if (pendingSkillInstalls[k].installed) doneCount++;
|
|
172
|
+
}
|
|
173
|
+
updateSkillListItems();
|
|
174
|
+
updateSkillInstallProgress(doneCount, totalCount);
|
|
175
|
+
|
|
176
|
+
if (doneCount === totalCount) {
|
|
177
|
+
skillInstallDone = true;
|
|
178
|
+
var hasUpdates = false;
|
|
179
|
+
for (var u = 0; u < pendingSkillInstalls.length; u++) {
|
|
180
|
+
if (pendingSkillInstalls[u].status === "outdated") { hasUpdates = true; break; }
|
|
181
|
+
}
|
|
182
|
+
var doneMsg = hasUpdates ? "All skills updated successfully." : "All skills installed successfully.";
|
|
183
|
+
skillInstallStatus.innerHTML = '<span class="skill-status-ok">' + iconHtml("circle-check") + '</span> ' + doneMsg;
|
|
184
|
+
refreshIcons();
|
|
185
|
+
skillInstallOk.disabled = false;
|
|
186
|
+
skillInstallOk.textContent = "Proceed";
|
|
187
|
+
skillInstallOk.className = "confirm-btn confirm-proceed";
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function requireSkills(opts, cb) {
|
|
192
|
+
fetch(store.getState().basePath + "api/check-skill-updates", {
|
|
193
|
+
method: "POST",
|
|
194
|
+
headers: { "Content-Type": "application/json" },
|
|
195
|
+
body: JSON.stringify({ skills: opts.skills }),
|
|
196
|
+
})
|
|
197
|
+
.then(function (res) { return res.json(); })
|
|
198
|
+
.then(function (data) {
|
|
199
|
+
var results = data.results || [];
|
|
200
|
+
var actionable = [];
|
|
201
|
+
for (var i = 0; i < results.length; i++) {
|
|
202
|
+
var r = results[i];
|
|
203
|
+
if (r.status === "missing" || r.status === "outdated") {
|
|
204
|
+
var orig = null;
|
|
205
|
+
for (var j = 0; j < opts.skills.length; j++) {
|
|
206
|
+
if (opts.skills[j].name === r.name) { orig = opts.skills[j]; break; }
|
|
207
|
+
}
|
|
208
|
+
if (!orig) continue;
|
|
209
|
+
actionable.push({
|
|
210
|
+
name: r.name,
|
|
211
|
+
url: orig.url,
|
|
212
|
+
scope: orig.scope || "global",
|
|
213
|
+
installed: false,
|
|
214
|
+
status: r.status,
|
|
215
|
+
installedVersion: r.installedVersion,
|
|
216
|
+
remoteVersion: r.remoteVersion,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (actionable.length === 0) { cb(); return; }
|
|
221
|
+
pendingSkillInstalls = actionable;
|
|
222
|
+
skillInstallCallback = cb;
|
|
223
|
+
renderSkillInstallDialog(opts, actionable);
|
|
224
|
+
})
|
|
225
|
+
.catch(function () { cb(); });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function requireClayMateInterview(cb) {
|
|
229
|
+
requireSkills({
|
|
230
|
+
title: "Skill Installation Required",
|
|
231
|
+
reason: "The Mate Interview skill is required to create a new Mate.",
|
|
232
|
+
skills: [{ name: "clay-mate-interview", url: "https://github.com/chadbyte/clay-mate-interview", scope: "global" }]
|
|
233
|
+
}, cb);
|
|
234
|
+
}
|
|
@@ -3,6 +3,25 @@ import { escapeHtml } from './utils.js';
|
|
|
3
3
|
import { refreshIcons } from './icons.js';
|
|
4
4
|
import { openSearch as openSessionSearch } from './session-search.js';
|
|
5
5
|
|
|
6
|
+
function formatRelativeDate(ts) {
|
|
7
|
+
if (!ts) return "";
|
|
8
|
+
var now = Date.now();
|
|
9
|
+
var diff = now - ts;
|
|
10
|
+
if (diff < 0) diff = 0;
|
|
11
|
+
var sec = Math.floor(diff / 1000);
|
|
12
|
+
if (sec < 60) return "just now";
|
|
13
|
+
var min = Math.floor(sec / 60);
|
|
14
|
+
if (min < 60) return min + "m ago";
|
|
15
|
+
var hr = Math.floor(min / 60);
|
|
16
|
+
if (hr < 24) return hr + "h ago";
|
|
17
|
+
var days = Math.floor(hr / 24);
|
|
18
|
+
if (days < 7) return days + "d ago";
|
|
19
|
+
if (days < 30) return Math.floor(days / 7) + "w ago";
|
|
20
|
+
var d = new Date(ts);
|
|
21
|
+
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
|
22
|
+
return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
|
|
23
|
+
}
|
|
24
|
+
|
|
6
25
|
var ctx;
|
|
7
26
|
var paletteEl = null;
|
|
8
27
|
var inputEl = null;
|
|
@@ -260,7 +279,7 @@ function renderHome(filter) {
|
|
|
260
279
|
iconHtml = s.projectIcon || '<i data-lucide="message-square"></i>';
|
|
261
280
|
}
|
|
262
281
|
var projLabel = (s.projectIcon || "") + " " + escapeHtml(s.projectTitle || s.projectSlug);
|
|
263
|
-
html += renderItem(flatIndex, iconHtml, escapeHtml(s.sessionTitle || "New Session"), projLabel.trim(), null);
|
|
282
|
+
html += renderItem(flatIndex, iconHtml, escapeHtml(s.sessionTitle || "New Session"), projLabel.trim(), null, s.lastActivity);
|
|
264
283
|
flatIndex++;
|
|
265
284
|
}
|
|
266
285
|
}
|
|
@@ -418,7 +437,7 @@ function appendHomeSearchResults(results, query) {
|
|
|
418
437
|
}
|
|
419
438
|
var projLabel = (r.projectIcon || "") + " " + escapeHtml(r.projectTitle || r.projectSlug);
|
|
420
439
|
var snippet = r.snippet ? escapeHtml(r.snippet) : "";
|
|
421
|
-
html += renderItem(flatIndex, srIconHtml, escapeHtml(r.sessionTitle || "New Session"), projLabel.trim(), snippet);
|
|
440
|
+
html += renderItem(flatIndex, srIconHtml, escapeHtml(r.sessionTitle || "New Session"), projLabel.trim(), snippet, r.lastActivity);
|
|
422
441
|
flatIndex++;
|
|
423
442
|
}
|
|
424
443
|
|
|
@@ -436,11 +455,15 @@ function appendHomeSearchResults(results, query) {
|
|
|
436
455
|
// Shared rendering / interaction helpers
|
|
437
456
|
// ==========================================
|
|
438
457
|
|
|
439
|
-
function renderItem(index, iconContent, title, desc, snippet) {
|
|
458
|
+
function renderItem(index, iconContent, title, desc, snippet, timestamp) {
|
|
459
|
+
var dateStr = formatRelativeDate(timestamp);
|
|
440
460
|
return '<div class="cmd-palette-item" data-index="' + index + '">' +
|
|
441
461
|
'<div class="cmd-palette-item-icon">' + iconContent + '</div>' +
|
|
442
462
|
'<div class="cmd-palette-item-body">' +
|
|
443
|
-
'<div class="cmd-palette-item-title">' +
|
|
463
|
+
'<div class="cmd-palette-item-title-row">' +
|
|
464
|
+
'<span class="cmd-palette-item-title">' + title + '</span>' +
|
|
465
|
+
(dateStr ? '<span class="cmd-palette-item-date">' + dateStr + '</span>' : '') +
|
|
466
|
+
'</div>' +
|
|
444
467
|
(desc || snippet ?
|
|
445
468
|
'<div class="cmd-palette-item-meta">' +
|
|
446
469
|
(desc ? '<span class="cmd-palette-item-project">' + desc + '</span>' : '') +
|
|
@@ -150,29 +150,39 @@ export function sendMessage() {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// Check for @mention: if a mate was selected, route to mention handler
|
|
153
|
+
// Exception: if we're in a DM with the same mate, send as regular message instead
|
|
153
154
|
var mention = parseMentionFromInput(text);
|
|
154
155
|
if (mention) {
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
156
|
+
var dmMateId = ctx.getDmMateId ? ctx.getDmMateId() : null;
|
|
157
|
+
if (dmMateId && dmMateId === mention.mateId) {
|
|
158
|
+
// In DM with this mate — strip mention chip and send as normal message
|
|
159
|
+
hideMentionMenu();
|
|
160
|
+
removeMentionChip();
|
|
161
|
+
text = mention.text;
|
|
162
|
+
// Fall through to normal message send below
|
|
163
|
+
} else {
|
|
164
|
+
hideMentionMenu();
|
|
165
|
+
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
166
|
+
var mentionImages = pendingImages.slice();
|
|
167
|
+
var mentionPastes = pendingPastes.map(function (p) { return p.text; });
|
|
168
|
+
// Prepend file paths to mention text (same pattern as regular messages)
|
|
169
|
+
var mentionFiles = pendingFiles.slice();
|
|
170
|
+
var mentionText = mention.text;
|
|
171
|
+
if (mentionFiles.length > 0) {
|
|
172
|
+
var mFilePaths = mentionFiles.map(function (f) { return "[Uploaded file: " + f.path + "]"; }).join("\n");
|
|
173
|
+
mentionText = mentionText ? mFilePaths + "\n\n" + mentionText : mFilePaths;
|
|
174
|
+
}
|
|
175
|
+
// Render user message with mention chip (same as history replay)
|
|
176
|
+
renderMentionUser({ mateName: mention.mateName, text: mentionText, images: mentionImages.length > 0 ? mentionImages : null, pastes: mentionPastes.length > 0 ? mentionPastes : null });
|
|
177
|
+
sendMention(mention.mateId, mentionText, mentionPastes, mentionImages);
|
|
178
|
+
ctx.inputEl.value = "";
|
|
179
|
+
stickyReapplyMention();
|
|
180
|
+
sendInputSync();
|
|
181
|
+
clearPendingImages();
|
|
182
|
+
autoResize();
|
|
183
|
+
ctx.inputEl.focus();
|
|
184
|
+
return;
|
|
165
185
|
}
|
|
166
|
-
// Render user message with mention chip (same as history replay)
|
|
167
|
-
renderMentionUser({ mateName: mention.mateName, text: mentionText, images: mentionImages.length > 0 ? mentionImages : null, pastes: mentionPastes.length > 0 ? mentionPastes : null });
|
|
168
|
-
sendMention(mention.mateId, mentionText, mentionPastes, mentionImages);
|
|
169
|
-
ctx.inputEl.value = "";
|
|
170
|
-
stickyReapplyMention();
|
|
171
|
-
sendInputSync();
|
|
172
|
-
clearPendingImages();
|
|
173
|
-
autoResize();
|
|
174
|
-
ctx.inputEl.focus();
|
|
175
|
-
return;
|
|
176
186
|
}
|
|
177
187
|
|
|
178
188
|
// Prepend file paths to text
|
|
@@ -197,6 +207,7 @@ export function sendMessage() {
|
|
|
197
207
|
return;
|
|
198
208
|
}
|
|
199
209
|
|
|
210
|
+
ctx.currentMsgTs = Date.now();
|
|
200
211
|
ctx.addUserMessage(text, images.length > 0 ? images : null, pastes.length > 0 ? pastes : null);
|
|
201
212
|
|
|
202
213
|
var payload = { type: "message", text: text || "" };
|