clay-server 2.5.0 → 2.6.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/bin/claude-relay.js +6 -0
- package/bin/cli.js +26 -8
- package/lib/cli-sessions.js +2 -7
- package/lib/config.js +78 -5
- package/lib/daemon.js +233 -2
- package/lib/notes.js +3 -2
- package/lib/project.js +465 -28
- package/lib/public/app.js +187 -24
- package/lib/public/css/base.css +61 -61
- package/lib/public/css/diff.css +3 -4
- package/lib/public/css/filebrowser.css +362 -2
- package/lib/public/css/icon-strip.css +317 -1
- package/lib/public/css/input.css +127 -50
- package/lib/public/css/messages.css +1 -1
- package/lib/public/css/mobile-nav.css +8 -4
- package/lib/public/css/overlays.css +9 -6
- package/lib/public/css/server-settings.css +67 -20
- package/lib/public/css/sidebar.css +10 -101
- package/lib/public/css/skills.css +730 -0
- package/lib/public/css/title-bar.css +82 -4
- package/lib/public/index.html +277 -70
- package/lib/public/modules/input.js +119 -56
- package/lib/public/modules/project-settings.js +906 -0
- package/lib/public/modules/server-settings.js +409 -53
- package/lib/public/modules/sidebar.js +720 -1
- package/lib/public/modules/skills.js +710 -0
- package/lib/public/modules/terminal.js +7 -0
- package/lib/public/modules/theme.js +88 -89
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +18 -7
- package/lib/server.js +305 -1
- package/lib/sessions.js +9 -4
- package/lib/utils.js +18 -0
- package/package.json +3 -2
|
@@ -6,6 +6,8 @@ var ctx;
|
|
|
6
6
|
// --- State ---
|
|
7
7
|
var pendingImages = []; // [{data: base64, mediaType: "image/png"}]
|
|
8
8
|
var pendingPastes = []; // [{text: string, preview: string}]
|
|
9
|
+
var pendingFiles = []; // [{name: string, path: string}]
|
|
10
|
+
var uploadingCount = 0;
|
|
9
11
|
var slashActiveIdx = -1;
|
|
10
12
|
var slashFiltered = [];
|
|
11
13
|
var isComposing = false;
|
|
@@ -23,7 +25,8 @@ export var builtinCommands = [
|
|
|
23
25
|
export function sendMessage() {
|
|
24
26
|
var text = ctx.inputEl.value.trim();
|
|
25
27
|
var images = pendingImages.slice();
|
|
26
|
-
if (!text && images.length === 0 && pendingPastes.length === 0) return;
|
|
28
|
+
if (!text && images.length === 0 && pendingPastes.length === 0 && pendingFiles.length === 0) return;
|
|
29
|
+
if (uploadingCount > 0) return; // wait for uploads to finish
|
|
27
30
|
hideSlashMenu();
|
|
28
31
|
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
29
32
|
|
|
@@ -78,6 +81,13 @@ export function sendMessage() {
|
|
|
78
81
|
return;
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
// Prepend file paths to text
|
|
85
|
+
var files = pendingFiles.slice();
|
|
86
|
+
if (files.length > 0) {
|
|
87
|
+
var filePaths = files.map(function (f) { return "[Uploaded file: " + f.path + "]"; }).join("\n");
|
|
88
|
+
text = text ? filePaths + "\n\n" + text : filePaths;
|
|
89
|
+
}
|
|
90
|
+
|
|
81
91
|
var pastes = pendingPastes.map(function (p) { return p.text; });
|
|
82
92
|
ctx.addUserMessage(text, images.length > 0 ? images : null, pastes.length > 0 ? pastes : null);
|
|
83
93
|
|
|
@@ -183,6 +193,7 @@ function removePendingImage(idx) {
|
|
|
183
193
|
export function clearPendingImages() {
|
|
184
194
|
pendingImages = [];
|
|
185
195
|
pendingPastes = [];
|
|
196
|
+
pendingFiles = [];
|
|
186
197
|
renderInputPreviews();
|
|
187
198
|
}
|
|
188
199
|
|
|
@@ -191,10 +202,15 @@ function removePendingPaste(idx) {
|
|
|
191
202
|
renderInputPreviews();
|
|
192
203
|
}
|
|
193
204
|
|
|
205
|
+
function removePendingFile(idx) {
|
|
206
|
+
pendingFiles.splice(idx, 1);
|
|
207
|
+
renderInputPreviews();
|
|
208
|
+
}
|
|
209
|
+
|
|
194
210
|
function renderInputPreviews() {
|
|
195
211
|
var bar = ctx.imagePreviewBar;
|
|
196
212
|
bar.innerHTML = "";
|
|
197
|
-
if (pendingImages.length === 0 && pendingPastes.length === 0) {
|
|
213
|
+
if (pendingImages.length === 0 && pendingPastes.length === 0 && pendingFiles.length === 0 && uploadingCount === 0) {
|
|
198
214
|
bar.classList.remove("visible");
|
|
199
215
|
return;
|
|
200
216
|
}
|
|
@@ -222,6 +238,45 @@ function renderInputPreviews() {
|
|
|
222
238
|
})(i);
|
|
223
239
|
}
|
|
224
240
|
|
|
241
|
+
// File chips
|
|
242
|
+
for (var fi = 0; fi < pendingFiles.length; fi++) {
|
|
243
|
+
(function (idx) {
|
|
244
|
+
var chip = document.createElement("div");
|
|
245
|
+
chip.className = "file-chip";
|
|
246
|
+
var icon = document.createElement("span");
|
|
247
|
+
icon.className = "file-chip-icon";
|
|
248
|
+
icon.innerHTML = iconHtml("file");
|
|
249
|
+
var nameSpan = document.createElement("span");
|
|
250
|
+
nameSpan.className = "file-chip-name";
|
|
251
|
+
nameSpan.textContent = pendingFiles[idx].name;
|
|
252
|
+
var removeBtn = document.createElement("button");
|
|
253
|
+
removeBtn.className = "file-chip-remove";
|
|
254
|
+
removeBtn.innerHTML = iconHtml("x");
|
|
255
|
+
removeBtn.addEventListener("click", function (e) {
|
|
256
|
+
e.stopPropagation();
|
|
257
|
+
removePendingFile(idx);
|
|
258
|
+
});
|
|
259
|
+
chip.appendChild(icon);
|
|
260
|
+
chip.appendChild(nameSpan);
|
|
261
|
+
chip.appendChild(removeBtn);
|
|
262
|
+
bar.appendChild(chip);
|
|
263
|
+
})(fi);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Uploading indicator
|
|
267
|
+
if (uploadingCount > 0) {
|
|
268
|
+
var chip = document.createElement("div");
|
|
269
|
+
chip.className = "file-chip file-chip-uploading";
|
|
270
|
+
var spinner = document.createElement("span");
|
|
271
|
+
spinner.className = "file-chip-spinner";
|
|
272
|
+
var label = document.createElement("span");
|
|
273
|
+
label.className = "file-chip-name";
|
|
274
|
+
label.textContent = "Uploading" + (uploadingCount > 1 ? " (" + uploadingCount + ")" : "") + "...";
|
|
275
|
+
chip.appendChild(spinner);
|
|
276
|
+
chip.appendChild(label);
|
|
277
|
+
bar.appendChild(chip);
|
|
278
|
+
}
|
|
279
|
+
|
|
225
280
|
// Pasted content chips
|
|
226
281
|
for (var j = 0; j < pendingPastes.length; j++) {
|
|
227
282
|
(function (idx) {
|
|
@@ -253,6 +308,46 @@ function renderInputPreviews() {
|
|
|
253
308
|
var MAX_IMAGE_BYTES = 5 * 1024 * 1024; // 5 MB
|
|
254
309
|
var RESIZE_MAX_DIM = 1920;
|
|
255
310
|
var RESIZE_QUALITY = 0.85;
|
|
311
|
+
var MAX_UPLOAD_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
312
|
+
|
|
313
|
+
// --- File upload ---
|
|
314
|
+
function uploadFile(file) {
|
|
315
|
+
if (file.size > MAX_UPLOAD_BYTES) {
|
|
316
|
+
if (ctx.addSystemMessage) ctx.addSystemMessage("File too large (max 50MB): " + file.name, true);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
uploadingCount++;
|
|
320
|
+
renderInputPreviews();
|
|
321
|
+
var reader = new FileReader();
|
|
322
|
+
reader.onload = function (ev) {
|
|
323
|
+
var dataUrl = ev.target.result;
|
|
324
|
+
var commaIdx = dataUrl.indexOf(",");
|
|
325
|
+
var b64 = commaIdx !== -1 ? dataUrl.substring(commaIdx + 1) : "";
|
|
326
|
+
|
|
327
|
+
var xhr = new XMLHttpRequest();
|
|
328
|
+
xhr.open("POST", ctx.basePath + "api/upload");
|
|
329
|
+
xhr.setRequestHeader("Content-Type", "application/json");
|
|
330
|
+
xhr.onload = function () {
|
|
331
|
+
uploadingCount--;
|
|
332
|
+
if (xhr.status === 200) {
|
|
333
|
+
try {
|
|
334
|
+
var resp = JSON.parse(xhr.responseText);
|
|
335
|
+
pendingFiles.push({ name: resp.name || file.name, path: resp.path });
|
|
336
|
+
} catch (e) {}
|
|
337
|
+
} else {
|
|
338
|
+
if (ctx.addSystemMessage) ctx.addSystemMessage("Upload failed: " + file.name, true);
|
|
339
|
+
}
|
|
340
|
+
renderInputPreviews();
|
|
341
|
+
};
|
|
342
|
+
xhr.onerror = function () {
|
|
343
|
+
uploadingCount--;
|
|
344
|
+
if (ctx.addSystemMessage) ctx.addSystemMessage("Upload failed: " + file.name, true);
|
|
345
|
+
renderInputPreviews();
|
|
346
|
+
};
|
|
347
|
+
xhr.send(JSON.stringify({ name: file.name, data: b64 }));
|
|
348
|
+
};
|
|
349
|
+
reader.readAsDataURL(file);
|
|
350
|
+
}
|
|
256
351
|
|
|
257
352
|
function readImageBlob(blob) {
|
|
258
353
|
var reader = new FileReader();
|
|
@@ -356,26 +451,10 @@ export function handleInputSync(text) {
|
|
|
356
451
|
isRemoteInput = false;
|
|
357
452
|
}
|
|
358
453
|
|
|
359
|
-
// --- Attach menu ---
|
|
360
|
-
var attachMenuOpen = false;
|
|
361
|
-
|
|
362
|
-
function toggleAttachMenu() {
|
|
363
|
-
var menu = document.getElementById("attach-menu");
|
|
364
|
-
if (!menu) return;
|
|
365
|
-
attachMenuOpen = !attachMenuOpen;
|
|
366
|
-
menu.classList.toggle("hidden", !attachMenuOpen);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function closeAttachMenu() {
|
|
370
|
-
var menu = document.getElementById("attach-menu");
|
|
371
|
-
if (menu) menu.classList.add("hidden");
|
|
372
|
-
attachMenuOpen = false;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
454
|
function createFileInput(accept, capture, multiple) {
|
|
376
455
|
var input = document.createElement("input");
|
|
377
456
|
input.type = "file";
|
|
378
|
-
input.accept = accept;
|
|
457
|
+
if (accept) input.accept = accept;
|
|
379
458
|
if (capture) input.setAttribute("capture", capture);
|
|
380
459
|
if (multiple) input.multiple = true;
|
|
381
460
|
input.style.display = "none";
|
|
@@ -386,6 +465,8 @@ function createFileInput(accept, capture, multiple) {
|
|
|
386
465
|
for (var i = 0; i < input.files.length; i++) {
|
|
387
466
|
if (input.files[i].type.indexOf("image/") === 0) {
|
|
388
467
|
readImageBlob(input.files[i]);
|
|
468
|
+
} else {
|
|
469
|
+
uploadFile(input.files[i]);
|
|
389
470
|
}
|
|
390
471
|
}
|
|
391
472
|
}
|
|
@@ -399,47 +480,24 @@ function createFileInput(accept, capture, multiple) {
|
|
|
399
480
|
export function initInput(_ctx) {
|
|
400
481
|
ctx = _ctx;
|
|
401
482
|
|
|
402
|
-
//
|
|
403
|
-
var
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
attachBtn.addEventListener("click", function (e) {
|
|
483
|
+
// File (clip) button — opens file picker for all types
|
|
484
|
+
var attachFileBtn = document.getElementById("attach-file-btn");
|
|
485
|
+
if (attachFileBtn) {
|
|
486
|
+
attachFileBtn.addEventListener("click", function (e) {
|
|
407
487
|
e.stopPropagation();
|
|
408
|
-
|
|
409
|
-
if (!isTouchDevice) {
|
|
410
|
-
createFileInput("image/*", null, true);
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
toggleAttachMenu();
|
|
488
|
+
createFileInput(null, null, true);
|
|
414
489
|
});
|
|
415
490
|
}
|
|
416
491
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
var photosBtn = document.getElementById("attach-photos");
|
|
426
|
-
if (photosBtn) {
|
|
427
|
-
photosBtn.addEventListener("click", function () {
|
|
428
|
-
closeAttachMenu();
|
|
492
|
+
// Image button — opens image picker (OS handles camera/gallery choice)
|
|
493
|
+
var attachImageBtn = document.getElementById("attach-image-btn");
|
|
494
|
+
if (attachImageBtn) {
|
|
495
|
+
attachImageBtn.addEventListener("click", function (e) {
|
|
496
|
+
e.stopPropagation();
|
|
429
497
|
createFileInput("image/*", null, true);
|
|
430
498
|
});
|
|
431
499
|
}
|
|
432
500
|
|
|
433
|
-
// Close attach menu when clicking outside
|
|
434
|
-
document.addEventListener("click", function (e) {
|
|
435
|
-
if (attachMenuOpen) {
|
|
436
|
-
var wrap = document.getElementById("attach-wrap");
|
|
437
|
-
if (wrap && !wrap.contains(e.target)) {
|
|
438
|
-
closeAttachMenu();
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
|
|
443
501
|
// Paste handler
|
|
444
502
|
document.addEventListener("paste", function (e) {
|
|
445
503
|
var cd = e.clipboardData;
|
|
@@ -453,6 +511,9 @@ export function initInput(_ctx) {
|
|
|
453
511
|
if (cd.files[i].type.indexOf("image/") === 0) {
|
|
454
512
|
found = true;
|
|
455
513
|
readImageBlob(cd.files[i]);
|
|
514
|
+
} else if (cd.files[i].name) {
|
|
515
|
+
found = true;
|
|
516
|
+
uploadFile(cd.files[i]);
|
|
456
517
|
}
|
|
457
518
|
}
|
|
458
519
|
}
|
|
@@ -466,6 +527,12 @@ export function initInput(_ctx) {
|
|
|
466
527
|
found = true;
|
|
467
528
|
readImageBlob(blob);
|
|
468
529
|
}
|
|
530
|
+
} else if (cd.items[i].kind === "file") {
|
|
531
|
+
var fileBlob = cd.items[i].getAsFile();
|
|
532
|
+
if (fileBlob && fileBlob.name) {
|
|
533
|
+
found = true;
|
|
534
|
+
uploadFile(fileBlob);
|
|
535
|
+
}
|
|
469
536
|
}
|
|
470
537
|
}
|
|
471
538
|
}
|
|
@@ -559,10 +626,6 @@ export function initInput(_ctx) {
|
|
|
559
626
|
return;
|
|
560
627
|
}
|
|
561
628
|
e.preventDefault();
|
|
562
|
-
// If suggestion chips are visible, accept chip instead of sending
|
|
563
|
-
if (ctx.acceptSuggestionChip && ctx.acceptSuggestionChip()) {
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
629
|
sendMessage();
|
|
567
630
|
}
|
|
568
631
|
});
|