clay-server 2.9.3 → 2.10.0-beta.1

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
@@ -1825,6 +1825,7 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
1825
1825
  scrollToBottom: function() { scrollToBottom(); },
1826
1826
  setActivity: function(text) { setActivity(text); },
1827
1827
  stopUrgentBlink: function() { stopUrgentBlink(); },
1828
+ showImageModal: showImageModal,
1828
1829
  getContextPercent: function() {
1829
1830
  var used = contextData.input;
1830
1831
  var win = contextData.contextWindow;
@@ -2896,8 +2897,8 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
2896
2897
  var tr = getTools()[msg.id];
2897
2898
  if (tr && tr.hidden) break; // skip hidden plan tools
2898
2899
  // Always call updateToolResult for Edit (to show diff from input), or when content exists
2899
- if (msg.content != null || (tr && tr.name === "Edit" && tr.input && tr.input.old_string)) {
2900
- updateToolResult(msg.id, msg.content || "", msg.is_error || false);
2900
+ if (msg.content != null || msg.images || (tr && tr.name === "Edit" && tr.input && tr.input.old_string)) {
2901
+ updateToolResult(msg.id, msg.content || "", msg.is_error || false, msg.images);
2901
2902
  }
2902
2903
  // Refresh file browser if an Edit/Write tool modified the open file
2903
2904
  if (!msg.is_error && tr && (tr.name === "Edit" || tr.name === "Write") && tr.input && tr.input.file_path) {
@@ -3450,7 +3451,17 @@ import { initAdmin, checkAdminAccess } from './modules/admin.js';
3450
3451
  if (reconnectTimer) return;
3451
3452
  reconnectTimer = setTimeout(function () {
3452
3453
  reconnectTimer = null;
3453
- connect();
3454
+ // Check if auth is still valid before reconnecting
3455
+ fetch("/info").then(function (res) {
3456
+ if (res.status === 401) {
3457
+ location.reload();
3458
+ return;
3459
+ }
3460
+ connect();
3461
+ }).catch(function () {
3462
+ // Server still down, try connecting anyway
3463
+ connect();
3464
+ });
3454
3465
  }, reconnectDelay);
3455
3466
  reconnectDelay = Math.min(reconnectDelay * 1.5, 10000);
3456
3467
  }
@@ -858,6 +858,25 @@ pre.mermaid-error {
858
858
  font-size: 12px;
859
859
  }
860
860
 
861
+ /* --- Image viewer (Read tool) --- */
862
+ .tool-result-image {
863
+ display: flex;
864
+ align-items: center;
865
+ justify-content: center;
866
+ padding: 12px;
867
+ }
868
+
869
+ .tool-result-image img {
870
+ max-width: 100%;
871
+ max-height: 400px;
872
+ border-radius: 8px;
873
+ border: 1px solid var(--border-subtle);
874
+ cursor: pointer;
875
+ transition: opacity 0.15s;
876
+ }
877
+
878
+ .tool-result-image img:hover { opacity: 0.85; }
879
+
861
880
  /* --- Code viewer (Read tool) --- */
862
881
  .code-viewer {
863
882
  display: flex;
@@ -1281,7 +1281,16 @@ function parseLineNumberedContent(text) {
1281
1281
  return { numbers: numbers, code: code };
1282
1282
  }
1283
1283
 
1284
- export function updateToolResult(id, content, isError) {
1284
+ var IMAGE_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp", ".ico"]);
1285
+
1286
+ function isImagePath(filePath) {
1287
+ if (!filePath) return false;
1288
+ var dotIdx = filePath.lastIndexOf(".");
1289
+ if (dotIdx === -1) return false;
1290
+ return IMAGE_EXTS.has(filePath.substring(dotIdx).toLowerCase());
1291
+ }
1292
+
1293
+ export function updateToolResult(id, content, isError, images) {
1285
1294
  var tool = tools[id];
1286
1295
  if (!tool) return;
1287
1296
 
@@ -1309,6 +1318,27 @@ export function updateToolResult(id, content, isError) {
1309
1318
  } else if (!isError && isDiffContent(displayContent)) {
1310
1319
  var patchLang = tool.input && tool.input.file_path ? getLanguageFromPath(tool.input.file_path) : null;
1311
1320
  resultBlock.appendChild(renderPatchDiff(displayContent, patchLang));
1321
+ } else if (!isError && tool.name === "Read" && tool.input && tool.input.file_path && isImagePath(tool.input.file_path)) {
1322
+ // Image file: show inline preview
1323
+ var imgWrap = document.createElement("div");
1324
+ imgWrap.className = "tool-result-image";
1325
+ var img = document.createElement("img");
1326
+ if (images && images.length > 0) {
1327
+ img.src = "data:" + images[0].mediaType + ";base64," + images[0].data;
1328
+ } else {
1329
+ img.src = "api/file?path=" + encodeURIComponent(tool.input.file_path);
1330
+ }
1331
+ img.alt = tool.input.file_path.split("/").pop();
1332
+ img.draggable = false;
1333
+ img.addEventListener("click", function (e) {
1334
+ e.stopPropagation();
1335
+ e.preventDefault();
1336
+ if (ctx.showImageModal) ctx.showImageModal(this.src);
1337
+ });
1338
+ imgWrap.appendChild(img);
1339
+ resultBlock.appendChild(imgWrap);
1340
+ resultBlock.className = "tool-result-block";
1341
+ tool.el.classList.add("expanded");
1312
1342
  } else if (!isError && tool.name === "Read" && tool.input && tool.input.file_path) {
1313
1343
  var parsed = parseLineNumberedContent(displayContent);
1314
1344
  if (parsed) {
package/lib/sdk-bridge.js CHANGED
@@ -279,6 +279,7 @@ function createSDKBridge(opts) {
279
279
  delete session.activeTaskToolIds[block.tool_use_id];
280
280
  }
281
281
  var resultText = "";
282
+ var resultImages = [];
282
283
  if (typeof block.content === "string") {
283
284
  resultText = block.content;
284
285
  } else if (Array.isArray(block.content)) {
@@ -286,14 +287,25 @@ function createSDKBridge(opts) {
286
287
  .filter(function(c) { return c.type === "text"; })
287
288
  .map(function(c) { return c.text; })
288
289
  .join("\n");
290
+ for (var ri = 0; ri < block.content.length; ri++) {
291
+ var rc = block.content[ri];
292
+ if (rc.type === "image" && rc.source) {
293
+ resultImages.push({
294
+ mediaType: rc.source.media_type,
295
+ data: rc.source.data,
296
+ });
297
+ }
298
+ }
289
299
  }
290
300
  session.sentToolResults[block.tool_use_id] = true;
291
- sendAndRecord(session, {
301
+ var toolResultMsg = {
292
302
  type: "tool_result",
293
303
  id: block.tool_use_id,
294
304
  content: resultText,
295
305
  is_error: block.is_error || false,
296
- });
306
+ };
307
+ if (resultImages.length > 0) toolResultMsg.images = resultImages;
308
+ sendAndRecord(session, toolResultMsg);
297
309
  }
298
310
  }
299
311
  }
package/lib/sessions.js CHANGED
@@ -325,6 +325,7 @@ function createSessionManager(opts) {
325
325
  if (!session) return;
326
326
 
327
327
  activeSessionId = localId;
328
+ if (targetWs) targetWs._clayActiveSession = localId;
328
329
 
329
330
  // In multi-user mode with a specific client, only send to that client
330
331
  var _send = (targetWs && sendTo) ? function (obj) { sendTo(targetWs, obj); } : send;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.9.3",
3
+ "version": "2.10.0-beta.1",
4
4
  "description": "Web UI for Claude Code. Any device. Push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",