clay-server 2.26.0-beta.18 → 2.26.0-beta.19

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.
@@ -111,7 +111,7 @@ function create(sendCommand, getTabList, contextOps) {
111
111
  // --- browser_screenshot ---
112
112
  tools.push(tool(
113
113
  "browser_screenshot",
114
- "Capture a screenshot of a browser tab (full viewport or a specific element)",
114
+ "Capture a screenshot of a browser tab. Skip if the tab is already attached as a context source (data is auto-injected).",
115
115
  buildShape({
116
116
  tabId: { type: "number", description: "Tab ID" },
117
117
  selector: { type: "string", description: "CSS selector to capture a specific element (optional)" },
@@ -134,7 +134,7 @@ function create(sendCommand, getTabList, contextOps) {
134
134
  // --- browser_console ---
135
135
  tools.push(tool(
136
136
  "browser_console",
137
- "Read captured console logs from a tab (log, warn, error, info)",
137
+ "Read captured console logs from a tab. Skip if the tab is already a context source (data is auto-injected).",
138
138
  buildShape({
139
139
  tabId: { type: "number", description: "Tab ID" },
140
140
  }, ["tabId"]),
@@ -157,7 +157,7 @@ function create(sendCommand, getTabList, contextOps) {
157
157
  // --- browser_network ---
158
158
  tools.push(tool(
159
159
  "browser_network",
160
- "Read captured network requests (fetch/XHR) from a tab",
160
+ "Read captured network requests (fetch/XHR) from a tab. Skip if the tab is already a context source.",
161
161
  buildShape({
162
162
  tabId: { type: "number", description: "Tab ID" },
163
163
  }, ["tabId"]),
@@ -181,7 +181,7 @@ function create(sendCommand, getTabList, contextOps) {
181
181
  // --- browser_read_page ---
182
182
  tools.push(tool(
183
183
  "browser_read_page",
184
- "Read page text content (innerText). Optionally read only a specific element.",
184
+ "Read page text content (innerText). Skip if the tab is already a context source (text is auto-injected). Use for tabs NOT in context sources, or to read a specific element via selector.",
185
185
  buildShape({
186
186
  tabId: { type: "number", description: "Tab ID" },
187
187
  selector: { type: "string", description: "CSS selector to read specific element (optional)" },
package/lib/project.js CHANGED
@@ -4236,7 +4236,8 @@ function createProjectContext(opts) {
4236
4236
  data: screenshotData,
4237
4237
  file: screenshotName,
4238
4238
  tabTitle: tabLabel,
4239
- tabUrl: tabInfo ? tabInfo.url : ""
4239
+ tabUrl: tabInfo ? tabInfo.url : "",
4240
+ tabFavIconUrl: tabInfo ? tabInfo.favIconUrl : ""
4240
4241
  });
4241
4242
  parts.push("[Screenshot saved: " + screenshotPath + "]");
4242
4243
  }
@@ -4258,7 +4259,8 @@ function createProjectContext(opts) {
4258
4259
  }
4259
4260
 
4260
4261
  if (tabContextParts.length > 0) {
4261
- fullText = tabContextParts.join("\n\n---\n\n") + "\n\n" + fullText;
4262
+ fullText = "[The following browser tab data is automatically attached as context sources. Do NOT call browser_read_page, browser_console, browser_network, or browser_screenshot for these tabs — the data is already here.]\n\n" +
4263
+ tabContextParts.join("\n\n---\n\n") + "\n\n" + fullText;
4262
4264
  }
4263
4265
 
4264
4266
  // If screenshots were captured, send context preview cards and add to SDK images
@@ -4272,6 +4274,7 @@ function createProjectContext(opts) {
4272
4274
  tab: {
4273
4275
  title: ss.tabTitle || "",
4274
4276
  url: ss.tabUrl || "",
4277
+ favIconUrl: ss.tabFavIconUrl || "",
4275
4278
  screenshotFile: ss.file
4276
4279
  }
4277
4280
  };
@@ -4282,6 +4285,7 @@ function createProjectContext(opts) {
4282
4285
  tab: {
4283
4286
  title: ss.tabTitle || "",
4284
4287
  url: ss.tabUrl || "",
4288
+ favIconUrl: ss.tabFavIconUrl || "",
4285
4289
  screenshotUrl: "/p/" + slug + "/images/" + ss.file
4286
4290
  }
4287
4291
  });
package/lib/public/app.js CHANGED
@@ -4388,6 +4388,8 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
4388
4388
  if (dhBar) dhBar.remove();
4389
4389
  var dbBadges = document.querySelectorAll(".debate-header-badge");
4390
4390
  for (var dbi = 0; dbi < dbBadges.length; dbi++) dbBadges[dbi].remove();
4391
+ // Clean up ended mode banner if debate is not active on this session
4392
+ if (debateEndedMode) exitDebateEndedMode();
4391
4393
  }
4392
4394
  scrollToBottom();
4393
4395
  // Scroll to tool element if navigating from file edit history
@@ -4741,7 +4743,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
4741
4743
  header.className = "context-card-header";
4742
4744
  var icon = document.createElement("span");
4743
4745
  icon.className = "context-card-icon";
4744
- icon.textContent = "\uD83D\uDC41";
4746
+ icon.innerHTML = iconHtml("globe");
4745
4747
  header.appendChild(icon);
4746
4748
  var label = document.createElement("span");
4747
4749
  label.textContent = "Viewing tab";
@@ -4765,6 +4767,15 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
4765
4767
  if (tabTitle || tabDomain) {
4766
4768
  var meta = document.createElement("div");
4767
4769
  meta.className = "context-card-meta";
4770
+ if (msg.tab.favIconUrl) {
4771
+ var fav = document.createElement("img");
4772
+ fav.className = "context-card-favicon";
4773
+ fav.src = msg.tab.favIconUrl;
4774
+ fav.width = 14;
4775
+ fav.height = 14;
4776
+ fav.onerror = function () { this.style.display = "none"; };
4777
+ meta.appendChild(fav);
4778
+ }
4768
4779
  var titleEl = document.createElement("span");
4769
4780
  titleEl.className = "context-card-title";
4770
4781
  titleEl.textContent = tabTitle;
@@ -873,7 +873,8 @@
873
873
  .terminal-tab.active { color: var(--text); border-color: var(--accent); }
874
874
  .terminal-tab.exited { opacity: 0.5; }
875
875
 
876
- .terminal-tab-close {
876
+
877
+ .terminal-tab-more {
877
878
  display: none;
878
879
  align-items: center;
879
880
  justify-content: center;
@@ -888,9 +889,44 @@
888
889
  line-height: 1;
889
890
  }
890
891
 
891
- .terminal-tab:hover .terminal-tab-close,
892
- .terminal-tab.active .terminal-tab-close { display: flex; }
893
- .terminal-tab-close:hover { background: rgba(var(--overlay-rgb),0.1); color: var(--text); }
892
+ .terminal-tab:hover .terminal-tab-more,
893
+ .terminal-tab.active .terminal-tab-more { display: flex; }
894
+ .terminal-tab-more:hover { background: rgba(var(--overlay-rgb),0.1); color: var(--text); }
895
+
896
+ .terminal-tab-ctx {
897
+ position: fixed;
898
+ z-index: 9999;
899
+ min-width: 120px;
900
+ background: var(--bg-elevated, var(--bg));
901
+ border: 1px solid var(--border);
902
+ border-radius: 6px;
903
+ padding: 4px;
904
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
905
+ }
906
+
907
+ .terminal-tab-ctx-item {
908
+ display: flex;
909
+ align-items: center;
910
+ gap: 8px;
911
+ width: 100%;
912
+ padding: 6px 10px;
913
+ border: none;
914
+ border-radius: 4px;
915
+ background: transparent;
916
+ color: var(--text-secondary);
917
+ font-size: 12px;
918
+ cursor: pointer;
919
+ white-space: nowrap;
920
+ }
921
+
922
+ .terminal-tab-ctx-item:hover {
923
+ background: rgba(var(--overlay-rgb),0.08);
924
+ color: var(--text);
925
+ }
926
+
927
+ .terminal-tab-ctx-danger:hover {
928
+ color: #e74c3c;
929
+ }
894
930
 
895
931
  .terminal-tab-label { cursor: default; }
896
932
 
@@ -971,6 +1007,7 @@
971
1007
 
972
1008
  .terminal-tab-body .xterm {
973
1009
  height: 100%;
1010
+ overflow: hidden;
974
1011
  }
975
1012
 
976
1013
  /* --- Terminal key toolbar (mobile) --- */
@@ -197,8 +197,13 @@
197
197
  }
198
198
 
199
199
  .context-card-header .context-card-icon {
200
- font-size: 13px;
201
200
  opacity: 0.7;
201
+ display: inline-flex;
202
+ align-items: center;
203
+ }
204
+ .context-card-header .context-card-icon .lucide {
205
+ width: 14px;
206
+ height: 14px;
202
207
  }
203
208
 
204
209
  .context-card-screenshot {
@@ -226,10 +231,16 @@
226
231
 
227
232
  .context-card-meta {
228
233
  display: flex;
229
- justify-content: space-between;
230
- align-items: baseline;
234
+ align-items: center;
231
235
  margin-top: 8px;
232
- gap: 12px;
236
+ gap: 6px;
237
+ }
238
+ .context-card-favicon {
239
+ width: 14px;
240
+ height: 14px;
241
+ border-radius: 2px;
242
+ flex-shrink: 0;
243
+ object-fit: contain;
233
244
  }
234
245
 
235
246
  .context-card-title {
@@ -240,6 +251,8 @@
240
251
  text-overflow: ellipsis;
241
252
  flex: 1;
242
253
  min-width: 0;
254
+ flex: 1;
255
+ min-width: 0;
243
256
  }
244
257
 
245
258
  .context-card-domain {
@@ -2040,6 +2040,7 @@
2040
2040
  <script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5/lib/xterm.min.js"></script>
2041
2041
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0/lib/addon-fit.min.js"></script>
2042
2042
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0/lib/addon-web-links.min.js"></script>
2043
+ <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0/lib/addon-webgl.min.js"></script>
2043
2044
  <script type="module" src="app.js"></script>
2044
2045
  <div id="pwa-install-modal" class="pwa-modal hidden">
2045
2046
  <div class="pwa-modal-backdrop"></div>
@@ -690,8 +690,8 @@ export function renderDebateEnded(entry) {
690
690
  ctx.messagesEl.appendChild(statusLine);
691
691
  refreshIcons();
692
692
 
693
- // Show ended mode with native input area
694
- if (ctx.showDebateEndedMode) ctx.showDebateEndedMode(entry);
693
+ // During history replay, don't activate ended mode banner.
694
+ // The debate is already over; status line in messages is sufficient.
695
695
  }
696
696
 
697
697
  export function renderDebateCommentInjected(entry) {
@@ -352,6 +352,16 @@ function createXtermForTab(tab) {
352
352
 
353
353
  xterm.open(bodyEl);
354
354
 
355
+ // WebGL addon: pixel-perfect rendering (eliminates gaps in block characters)
356
+ // Must be loaded after xterm.open() so the rendering context is available.
357
+ if (typeof WebglAddon !== "undefined") {
358
+ try {
359
+ xterm.loadAddon(new WebglAddon.WebglAddon());
360
+ } catch (e) {
361
+ // WebGL not available, fall back to DOM renderer
362
+ }
363
+ }
364
+
355
365
  // Route input to server
356
366
  xterm.onData(function (data) {
357
367
  if (ctx.ws && ctx.connected) {
@@ -481,14 +491,14 @@ function renderTabBar() {
481
491
  startRenameTab(t, label);
482
492
  });
483
493
 
484
- var closeBtn = document.createElement("button");
485
- closeBtn.className = "terminal-tab-close";
486
- closeBtn.innerHTML = '<i data-lucide="trash-2" style="width:12px;height:12px"></i>';
487
- closeBtn.addEventListener("click", function (e) {
494
+ var moreBtn = document.createElement("button");
495
+ moreBtn.className = "terminal-tab-more";
496
+ moreBtn.innerHTML = '<i data-lucide="ellipsis" style="width:12px;height:12px"></i>';
497
+ moreBtn.addEventListener("click", function (e) {
488
498
  e.stopPropagation();
489
- closeTab(t.id);
499
+ showTabContextMenu(e, t, label);
490
500
  });
491
- el.appendChild(closeBtn);
501
+ el.appendChild(moreBtn);
492
502
 
493
503
  el.addEventListener("click", function () {
494
504
  if (t.id !== activeTabId) {
@@ -504,6 +514,52 @@ function renderTabBar() {
504
514
  refreshIcons();
505
515
  }
506
516
 
517
+ // --- Tab context menu (three-dot) ---
518
+ function showTabContextMenu(e, tab, labelEl) {
519
+ var existing = document.querySelector(".terminal-tab-ctx");
520
+ if (existing) existing.remove();
521
+
522
+ var menu = document.createElement("div");
523
+ menu.className = "terminal-tab-ctx";
524
+
525
+ var renameItem = document.createElement("button");
526
+ renameItem.className = "terminal-tab-ctx-item";
527
+ renameItem.innerHTML = '<i data-lucide="pencil" style="width:13px;height:13px"></i> Rename';
528
+ renameItem.addEventListener("click", function () {
529
+ menu.remove();
530
+ startRenameTab(tab, labelEl);
531
+ });
532
+ menu.appendChild(renameItem);
533
+
534
+ var closeItem = document.createElement("button");
535
+ closeItem.className = "terminal-tab-ctx-item terminal-tab-ctx-danger";
536
+ closeItem.innerHTML = '<i data-lucide="trash-2" style="width:13px;height:13px"></i> Close';
537
+ closeItem.addEventListener("click", function () {
538
+ menu.remove();
539
+ closeTab(tab.id);
540
+ });
541
+ menu.appendChild(closeItem);
542
+
543
+ document.body.appendChild(menu);
544
+ refreshIcons();
545
+
546
+ // Position near the button
547
+ var rect = e.currentTarget.getBoundingClientRect();
548
+ menu.style.top = (rect.bottom + 4) + "px";
549
+ menu.style.left = rect.left + "px";
550
+
551
+ // Dismiss on outside click
552
+ function dismiss(ev) {
553
+ if (!menu.contains(ev.target)) {
554
+ menu.remove();
555
+ document.removeEventListener("mousedown", dismiss, true);
556
+ }
557
+ }
558
+ setTimeout(function () {
559
+ document.addEventListener("mousedown", dismiss, true);
560
+ }, 0);
561
+ }
562
+
507
563
  // --- Rename tab inline ---
508
564
  function startRenameTab(tab, labelEl) {
509
565
  var input = document.createElement("input");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "2.26.0-beta.18",
3
+ "version": "2.26.0-beta.19",
4
4
  "description": "Self-hosted Claude Code in your browser. Multi-session, multi-user, push notifications.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",