clay-server 2.26.0-beta.5 → 2.26.0-beta.7

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.
@@ -123,6 +123,187 @@ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--acc
123
123
  }
124
124
  .pwa-standalone .top-bar-share-btn { display: none !important; }
125
125
 
126
+ /* Extension pill button — same style as share/install pills */
127
+ .ext-pill-wrap {
128
+ position: relative;
129
+ display: flex;
130
+ align-items: center;
131
+ }
132
+ .top-bar-ext-btn {
133
+ display: inline-flex;
134
+ align-items: center;
135
+ gap: 4px;
136
+ background: color-mix(in srgb, var(--text-muted) 10%, transparent);
137
+ color: var(--text-secondary);
138
+ border: none;
139
+ border-radius: 10px;
140
+ padding: 2px 10px;
141
+ font-family: inherit;
142
+ font-size: 11px;
143
+ font-weight: 600;
144
+ cursor: pointer;
145
+ white-space: nowrap;
146
+ line-height: 1;
147
+ transition: background 0.15s, color 0.15s;
148
+ }
149
+ .top-bar-ext-btn .lucide { width: 12px; height: 12px; }
150
+ .top-bar-ext-btn:hover { background: color-mix(in srgb, var(--text-muted) 18%, transparent); color: var(--text); }
151
+ @media (max-width: 768px) {
152
+ .top-bar-ext-btn span { display: none; }
153
+ }
154
+
155
+ /* Extension popover */
156
+ .ext-popover {
157
+ display: none;
158
+ position: absolute;
159
+ top: calc(100% + 8px);
160
+ left: 0;
161
+ background: var(--code-bg);
162
+ border: 1px solid var(--border);
163
+ border-radius: 12px;
164
+ padding: 16px;
165
+ z-index: 200;
166
+ box-shadow: 0 8px 32px rgba(var(--shadow-rgb), 0.35);
167
+ width: 340px;
168
+ }
169
+ .ext-popover.visible { display: block; }
170
+ .ext-popover-header { margin-bottom: 8px; }
171
+ .ext-popover-title {
172
+ font-size: 14px;
173
+ font-weight: 700;
174
+ color: var(--text);
175
+ }
176
+ .ext-experimental {
177
+ font-size: 10px;
178
+ font-weight: 600;
179
+ color: var(--warning, #f59e0b);
180
+ background: color-mix(in srgb, var(--warning, #f59e0b) 12%, transparent);
181
+ padding: 2px 6px;
182
+ border-radius: 6px;
183
+ vertical-align: middle;
184
+ letter-spacing: 0.3px;
185
+ text-transform: uppercase;
186
+ position: relative;
187
+ top: -1px;
188
+ }
189
+ .ext-popover-sub {
190
+ font-size: 11px;
191
+ color: var(--text-muted);
192
+ margin-top: 2px;
193
+ }
194
+ .ext-popover-sub a {
195
+ color: var(--accent);
196
+ text-decoration: none;
197
+ }
198
+ .ext-popover-sub a:hover { text-decoration: underline; }
199
+ .ext-popover-desc {
200
+ font-size: 12px;
201
+ color: var(--text-secondary);
202
+ line-height: 1.5;
203
+ margin-bottom: 12px;
204
+ }
205
+ .ext-popover-download {
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ gap: 6px;
210
+ width: 100%;
211
+ padding: 8px 0;
212
+ background: var(--accent);
213
+ color: #fff;
214
+ border: none;
215
+ border-radius: 8px;
216
+ font-family: inherit;
217
+ font-size: 12.5px;
218
+ font-weight: 600;
219
+ cursor: pointer;
220
+ transition: opacity 0.15s;
221
+ }
222
+ .ext-popover-download .lucide { width: 14px; height: 14px; }
223
+ .ext-popover-download:hover { opacity: 0.85; }
224
+ .ext-popover-download:disabled { opacity: 0.5; cursor: default; }
225
+ .ext-popover-status {
226
+ font-size: 11px;
227
+ color: var(--accent);
228
+ text-align: center;
229
+ margin-top: 6px;
230
+ }
231
+ .ext-popover-status.hidden { display: none; }
232
+ .ext-popover-divider {
233
+ height: 1px;
234
+ background: var(--border);
235
+ margin: 12px 0;
236
+ }
237
+ .ext-popover-guide-title {
238
+ font-size: 11px;
239
+ font-weight: 700;
240
+ color: var(--text-muted);
241
+ text-transform: uppercase;
242
+ letter-spacing: 0.5px;
243
+ margin-bottom: 8px;
244
+ }
245
+ .ext-popover-steps {
246
+ display: flex;
247
+ flex-direction: column;
248
+ gap: 6px;
249
+ }
250
+ .ext-popover-step {
251
+ font-size: 12px;
252
+ color: var(--text-secondary);
253
+ line-height: 1.5;
254
+ display: flex;
255
+ gap: 8px;
256
+ align-items: baseline;
257
+ }
258
+ .ext-snum {
259
+ display: inline-flex;
260
+ align-items: center;
261
+ justify-content: center;
262
+ width: 18px;
263
+ height: 18px;
264
+ border-radius: 50%;
265
+ background: var(--accent);
266
+ color: #fff;
267
+ font-size: 10px;
268
+ font-weight: 700;
269
+ flex-shrink: 0;
270
+ position: relative;
271
+ top: 1px;
272
+ }
273
+ .ext-popover-code {
274
+ font-family: "Roboto Mono", monospace;
275
+ font-size: 11px;
276
+ background: var(--bg-tertiary);
277
+ padding: 1px 5px;
278
+ border-radius: 4px;
279
+ cursor: pointer;
280
+ }
281
+ .ext-popover-code:hover { background: color-mix(in srgb, var(--accent) 15%, var(--bg-tertiary)); }
282
+
283
+ /* Extension connected state */
284
+ .top-bar-ext-btn.ext-connected {
285
+ background: color-mix(in srgb, var(--success, #22c55e) 12%, transparent);
286
+ color: var(--success, #22c55e);
287
+ }
288
+ .top-bar-ext-btn.ext-connected:hover {
289
+ background: color-mix(in srgb, var(--success, #22c55e) 20%, transparent);
290
+ color: var(--success, #22c55e);
291
+ }
292
+ .ext-popover-connected {
293
+ display: flex;
294
+ align-items: center;
295
+ gap: 6px;
296
+ padding: 8px 10px;
297
+ background: color-mix(in srgb, var(--success, #22c55e) 10%, transparent);
298
+ color: var(--success, #22c55e);
299
+ border-radius: 8px;
300
+ font-size: 12.5px;
301
+ font-weight: 600;
302
+ margin-bottom: 12px;
303
+ }
304
+ .ext-popover-connected .lucide { width: 15px; height: 15px; }
305
+ .ext-popover-connected.hidden { display: none; }
306
+
126
307
  /* PWA install modal */
127
308
  .pwa-modal {
128
309
  position: fixed;
@@ -171,6 +171,85 @@
171
171
 
172
172
  .sys-msg.error .sys-text { color: var(--error); }
173
173
 
174
+ /* ==========================================================================
175
+ Context Card (browser tab preview shown between user msg and assistant response)
176
+ ========================================================================== */
177
+
178
+ .context-card {
179
+ max-width: 400px;
180
+ margin: 8px auto 12px;
181
+ padding: 12px;
182
+ background: var(--bg-alt);
183
+ border: 1px solid var(--border);
184
+ border-radius: 10px;
185
+ box-shadow: 0 1px 3px rgba(var(--shadow-rgb), 0.08);
186
+ }
187
+
188
+ .context-card-header {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 6px;
192
+ margin-bottom: 10px;
193
+ font-size: 11px;
194
+ color: var(--text-muted);
195
+ letter-spacing: 0.02em;
196
+ font-weight: 500;
197
+ }
198
+
199
+ .context-card-header .context-card-icon {
200
+ font-size: 13px;
201
+ opacity: 0.7;
202
+ }
203
+
204
+ .context-card-screenshot {
205
+ display: block;
206
+ width: 100%;
207
+ max-height: 200px;
208
+ object-fit: contain;
209
+ border-radius: 8px;
210
+ background: var(--bg);
211
+ cursor: pointer;
212
+ transition: transform 0.15s ease;
213
+ }
214
+
215
+ .context-card-screenshot:hover {
216
+ transform: scale(1.01);
217
+ }
218
+
219
+ .context-card-skeleton {
220
+ width: 100%;
221
+ height: 120px;
222
+ border-radius: 8px;
223
+ background: var(--bg);
224
+ animation: skeleton-shimmer 1.5s ease-in-out infinite;
225
+ }
226
+
227
+ .context-card-meta {
228
+ display: flex;
229
+ justify-content: space-between;
230
+ align-items: baseline;
231
+ margin-top: 8px;
232
+ gap: 12px;
233
+ }
234
+
235
+ .context-card-title {
236
+ font-size: 12px;
237
+ color: var(--text-muted);
238
+ white-space: nowrap;
239
+ overflow: hidden;
240
+ text-overflow: ellipsis;
241
+ flex: 1;
242
+ min-width: 0;
243
+ }
244
+
245
+ .context-card-domain {
246
+ font-size: 11px;
247
+ color: var(--text-dimmer);
248
+ font-family: monospace;
249
+ white-space: nowrap;
250
+ flex-shrink: 0;
251
+ }
252
+
174
253
  /* ==========================================================================
175
254
  Activity Indicator
176
255
  ========================================================================== */
@@ -906,3 +906,4 @@
906
906
  line-height: 1.45;
907
907
  margin-top: 2px;
908
908
  }
909
+
@@ -23,8 +23,8 @@
23
23
  display: inline-flex;
24
24
  align-items: center;
25
25
  gap: 4px;
26
- background: color-mix(in srgb, var(--success) 12%, transparent);
27
- color: var(--success);
26
+ background: color-mix(in srgb, #3b82f6 12%, transparent);
27
+ color: #3b82f6;
28
28
  border: none;
29
29
  border-radius: 10px;
30
30
  padding: 2px 10px;
@@ -38,7 +38,7 @@
38
38
  }
39
39
 
40
40
  .top-bar-update-btn .lucide { width: 12px; height: 12px; }
41
- .top-bar-update-btn:hover { background: color-mix(in srgb, var(--success) 20%, transparent); }
41
+ .top-bar-update-btn:hover { background: color-mix(in srgb, #3b82f6 20%, transparent); }
42
42
 
43
43
  /* --- Top bar actions (right-aligned group) --- */
44
44
  .top-bar-actions {
@@ -52,6 +52,28 @@
52
52
  <div id="top-bar">
53
53
  <div class="top-bar-left-pills">
54
54
  <button id="pwa-install-pill" class="top-bar-install-btn hidden" title="Open as app"><i data-lucide="download"></i> Open as app</button>
55
+ <div id="ext-pill-wrap" class="ext-pill-wrap">
56
+ <button id="ext-pill" class="top-bar-ext-btn" title="Chrome Extension"><i data-lucide="puzzle"></i> Extension</button>
57
+ <div id="ext-popover" class="ext-popover">
58
+ <div class="ext-popover-header">
59
+ <div class="ext-popover-title">Clay for Chrome <span class="ext-experimental">Experimental</span></div>
60
+ <div class="ext-popover-sub">v0.1.0 &middot; <a href="https://github.com/chadbyte/clay-chrome" target="_blank" rel="noopener">GitHub</a></div>
61
+ </div>
62
+ <div class="ext-popover-connected hidden" id="ext-connected-banner"><i data-lucide="check-circle"></i> Extension connected</div>
63
+ <div class="ext-popover-desc" id="ext-popover-desc">Connect your browser to Clay. Gives Claude visibility into open tabs, console, network, DOM, and screenshots.</div>
64
+ <button class="ext-popover-download" id="ext-download-btn"><i data-lucide="download"></i> Download Extension (.zip)</button>
65
+ <div class="ext-popover-status hidden" id="ext-download-status"></div>
66
+ <div class="ext-popover-divider" id="ext-popover-divider"></div>
67
+ <div class="ext-popover-guide-title" id="ext-popover-guide-title">Installation</div>
68
+ <div class="ext-popover-steps">
69
+ <div class="ext-popover-step"><span class="ext-snum">1</span> Download above, then unzip to a permanent folder.</div>
70
+ <div class="ext-popover-step"><span class="ext-snum">2</span> Open <code id="ext-copy-url" class="ext-popover-code" title="Click to copy">chrome://extensions</code></div>
71
+ <div class="ext-popover-step"><span class="ext-snum">3</span> Enable <strong>Developer mode</strong> (top-right toggle).</div>
72
+ <div class="ext-popover-step"><span class="ext-snum">4</span> <strong>Load unpacked</strong> > select the unzipped folder.</div>
73
+ <div class="ext-popover-step"><span class="ext-snum">5</span> Refresh this page. Done!</div>
74
+ </div>
75
+ </div>
76
+ </div>
55
77
  <button id="share-pill" class="top-bar-share-btn" title="Share"><i data-lucide="qr-code"></i> Share</button>
56
78
  <div id="update-pill-wrap" class="top-bar-update hidden">
57
79
  <button id="update-pill" class="top-bar-update-btn"><i data-lucide="arrow-up-circle"></i> <span id="update-version"></span> is available. Update now</button>
@@ -420,9 +442,9 @@
420
442
  <button id="context-sources-add" type="button" title="Add context source"><i data-lucide="plus"></i><span>Context Sources</span></button>
421
443
  <div id="context-sources-picker" class="hidden">
422
444
  <div class="context-picker-section" id="context-picker-terminals"></div>
445
+ <div class="context-picker-section" id="context-picker-tabs"></div>
423
446
  </div>
424
447
  </div>
425
- <div id="suggestion-chips" class="hidden"></div>
426
448
  <div id="input-row">
427
449
  <div id="context-mini" class="hidden">
428
450
  <div class="context-mini-bar">
@@ -431,6 +453,7 @@
431
453
  <span class="context-mini-label" id="context-mini-label">0%</span>
432
454
  </div>
433
455
  <div id="image-preview-bar"></div>
456
+ <div id="suggestion-chips" class="hidden"></div>
434
457
  <textarea id="input" rows="1" placeholder="Message Claude Code..." enterkeyhint="send" dir="auto"></textarea>
435
458
  <div id="input-bottom">
436
459
  <div id="attach-wrap">
@@ -1,8 +1,9 @@
1
- // Context Sources — attach terminal output (and future browser tabs) as context for Claude
1
+ // Context Sources — attach terminal output and browser tabs as context for Claude
2
2
 
3
3
  var ctx = null;
4
4
  var activeSourceIds = new Set();
5
5
  var terminalList = []; // synced from terminal module's term_list
6
+ var browserTabList = []; // synced from Chrome extension via postMessage
6
7
 
7
8
  export function initContextSources(_ctx) {
8
9
  ctx = _ctx;
@@ -79,6 +80,36 @@ export function updateTerminalList(terminals) {
79
80
  }
80
81
  }
81
82
 
83
+ // Called when Chrome extension sends tab list via postMessage
84
+ export function updateBrowserTabList(tabs) {
85
+ browserTabList = tabs || [];
86
+
87
+ // Remove active tab sources that no longer exist
88
+ var changed = false;
89
+ for (var id of activeSourceIds) {
90
+ if (id.startsWith("tab:")) {
91
+ var tabId = parseInt(id.split(":")[1], 10);
92
+ var found = false;
93
+ for (var i = 0; i < browserTabList.length; i++) {
94
+ if (browserTabList[i].id === tabId) { found = true; break; }
95
+ }
96
+ if (!found) {
97
+ activeSourceIds.delete(id);
98
+ changed = true;
99
+ }
100
+ }
101
+ }
102
+
103
+ if (changed) saveToServer();
104
+ renderChips();
105
+
106
+ // If picker is open, re-render it
107
+ var picker = document.getElementById("context-sources-picker");
108
+ if (!picker.classList.contains("hidden")) {
109
+ renderPicker();
110
+ }
111
+ }
112
+
82
113
  function toggleSource(sourceId) {
83
114
  if (activeSourceIds.has(sourceId)) {
84
115
  activeSourceIds.delete(sourceId);
@@ -150,42 +181,87 @@ function renderChips() {
150
181
  }
151
182
 
152
183
  function renderPicker() {
153
- var section = document.getElementById("context-picker-terminals");
154
- section.innerHTML = "";
184
+ // --- Terminals section ---
185
+ var termSection = document.getElementById("context-picker-terminals");
186
+ termSection.innerHTML = "";
155
187
 
156
- var sectionLabel = document.createElement("div");
157
- sectionLabel.className = "context-picker-section-label";
158
- sectionLabel.textContent = "Terminals";
159
- section.appendChild(sectionLabel);
188
+ var termLabel = document.createElement("div");
189
+ termLabel.className = "context-picker-section-label";
190
+ termLabel.textContent = "Terminals";
191
+ termSection.appendChild(termLabel);
160
192
 
161
193
  if (terminalList.length === 0) {
162
- var empty = document.createElement("div");
163
- empty.className = "context-picker-empty";
164
- empty.textContent = "No terminals open";
165
- section.appendChild(empty);
166
- return;
167
- }
194
+ var termEmpty = document.createElement("div");
195
+ termEmpty.className = "context-picker-empty";
196
+ termEmpty.textContent = "No terminals open";
197
+ termSection.appendChild(termEmpty);
198
+ } else {
199
+ for (var i = 0; i < terminalList.length; i++) {
200
+ var term = terminalList[i];
201
+ var termSourceId = "term:" + term.id;
202
+ var termActive = activeSourceIds.has(termSourceId);
168
203
 
169
- for (var i = 0; i < terminalList.length; i++) {
170
- var term = terminalList[i];
171
- var sourceId = "term:" + term.id;
172
- var isActive = activeSourceIds.has(sourceId);
204
+ var termItem = document.createElement("div");
205
+ termItem.className = "context-picker-item" + (termActive ? " active" : "");
206
+ termItem.setAttribute("data-source-id", termSourceId);
173
207
 
174
- var item = document.createElement("div");
175
- item.className = "context-picker-item" + (isActive ? " active" : "");
176
- item.setAttribute("data-source-id", sourceId);
208
+ termItem.innerHTML =
209
+ '<i data-lucide="square-terminal"></i>' +
210
+ '<span>' + escapeHtml(term.title || ("Terminal " + term.id)) + '</span>' +
211
+ '<i data-lucide="check" class="context-picker-check"></i>';
177
212
 
178
- item.innerHTML =
179
- '<i data-lucide="square-terminal"></i>' +
180
- '<span>' + escapeHtml(term.title || ("Terminal " + term.id)) + '</span>' +
181
- '<i data-lucide="check" class="context-picker-check"></i>';
213
+ termItem.addEventListener("click", function() {
214
+ toggleSource(this.getAttribute("data-source-id"));
215
+ if (typeof lucide !== "undefined") lucide.createIcons();
216
+ });
182
217
 
183
- item.addEventListener("click", function() {
184
- toggleSource(this.getAttribute("data-source-id"));
185
- if (typeof lucide !== "undefined") lucide.createIcons();
186
- });
218
+ termSection.appendChild(termItem);
219
+ }
220
+ }
221
+
222
+ // --- Browser Tabs section ---
223
+ var tabSection = document.getElementById("context-picker-tabs");
224
+ tabSection.innerHTML = "";
225
+
226
+ if (browserTabList.length > 0) {
227
+ var tabLabel = document.createElement("div");
228
+ tabLabel.className = "context-picker-section-label";
229
+ tabLabel.textContent = "Browser Tabs";
230
+ tabSection.appendChild(tabLabel);
231
+
232
+ for (var j = 0; j < browserTabList.length; j++) {
233
+ var tab = browserTabList[j];
234
+ var tabSourceId = "tab:" + tab.id;
235
+ var tabActive = activeSourceIds.has(tabSourceId);
236
+
237
+ var tabItem = document.createElement("div");
238
+ tabItem.className = "context-picker-item" + (tabActive ? " active" : "");
239
+ tabItem.setAttribute("data-source-id", tabSourceId);
240
+
241
+ var tabTitle = tab.title || tab.url || "Tab";
242
+ // Truncate long URLs for display
243
+ var tabDisplay = tabTitle.length > 50 ? tabTitle.slice(0, 47) + "..." : tabTitle;
244
+
245
+ var faviconHtml = "";
246
+ if (tab.favIconUrl) {
247
+ faviconHtml = '<img src="' + escapeHtml(tab.favIconUrl) + '" class="context-picker-favicon" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'\'">' +
248
+ '<i data-lucide="globe" style="display:none"></i>';
249
+ } else {
250
+ faviconHtml = '<i data-lucide="globe"></i>';
251
+ }
252
+
253
+ tabItem.innerHTML =
254
+ faviconHtml +
255
+ '<span title="' + escapeHtml(tab.url || "") + '">' + escapeHtml(tabDisplay) + '</span>' +
256
+ '<i data-lucide="check" class="context-picker-check"></i>';
257
+
258
+ tabItem.addEventListener("click", function() {
259
+ toggleSource(this.getAttribute("data-source-id"));
260
+ if (typeof lucide !== "undefined") lucide.createIcons();
261
+ });
187
262
 
188
- section.appendChild(item);
263
+ tabSection.appendChild(tabItem);
264
+ }
189
265
  }
190
266
 
191
267
  if (typeof lucide !== "undefined") lucide.createIcons();
@@ -201,11 +277,22 @@ function getSourceLabel(id) {
201
277
  }
202
278
  return "Terminal " + termId;
203
279
  }
280
+ if (id.startsWith("tab:")) {
281
+ var tabId = parseInt(id.split(":")[1], 10);
282
+ for (var j = 0; j < browserTabList.length; j++) {
283
+ if (browserTabList[j].id === tabId) {
284
+ var title = browserTabList[j].title || browserTabList[j].url || "";
285
+ return title.length > 30 ? title.slice(0, 27) + "..." : title;
286
+ }
287
+ }
288
+ return "Tab " + tabId;
289
+ }
204
290
  return id;
205
291
  }
206
292
 
207
293
  function getSourceIcon(id) {
208
294
  if (id.startsWith("term:")) return "square-terminal";
295
+ if (id.startsWith("tab:")) return "globe";
209
296
  return "circle";
210
297
  }
211
298
 
@@ -1,4 +1,4 @@
1
- import { copyToClipboard } from './utils.js';
1
+ import { copyToClipboard, showToast } from './utils.js';
2
2
  import { iconHtml, refreshIcons } from './icons.js';
3
3
 
4
4
  var ctx;
@@ -155,6 +155,114 @@ export function initNotifications(_ctx) {
155
155
  });
156
156
  })();
157
157
 
158
+ // --- Extension pill popover ---
159
+ (function () {
160
+ var extPillWrap = $("ext-pill-wrap");
161
+ var extPillBtn = $("ext-pill");
162
+ var extPopover = $("ext-popover");
163
+ var extDownloadBtn = $("ext-download-btn");
164
+ var extDownloadStatus = $("ext-download-status");
165
+ var extCopyUrl = $("ext-copy-url");
166
+ if (!extPillWrap || !extPillBtn || !extPopover) return;
167
+
168
+ // Detect extension connection via postMessage from content.js
169
+ var extConnected = false;
170
+ var connectedBanner = $("ext-connected-banner");
171
+ var extDesc = $("ext-popover-desc");
172
+ var extDivider = $("ext-popover-divider");
173
+ var extGuideTitle = $("ext-popover-guide-title");
174
+ var extSteps = extPopover.querySelector(".ext-popover-steps");
175
+
176
+ function setExtConnected() {
177
+ if (extConnected) return;
178
+ extConnected = true;
179
+ extPillBtn.classList.add("ext-connected");
180
+ extPillBtn.innerHTML = '<i data-lucide="check"></i> Extension';
181
+ refreshIcons(extPillBtn);
182
+ if (connectedBanner) connectedBanner.classList.remove("hidden");
183
+ if (extDownloadBtn) extDownloadBtn.style.display = "none";
184
+ if (extDownloadStatus) extDownloadStatus.classList.add("hidden");
185
+ if (extDesc) extDesc.style.display = "none";
186
+ if (extDivider) extDivider.style.display = "none";
187
+ if (extGuideTitle) extGuideTitle.style.display = "none";
188
+ if (extSteps) extSteps.style.display = "none";
189
+ }
190
+
191
+ window.addEventListener("message", function (event) {
192
+ if (event.source !== window) return;
193
+ if (!event.data || event.data.source !== "clay-chrome-extension") return;
194
+ setExtConnected();
195
+ });
196
+
197
+ // Toggle popover
198
+ extPillBtn.addEventListener("click", function (e) {
199
+ e.stopPropagation();
200
+ extPopover.classList.toggle("visible");
201
+ refreshIcons(extPopover);
202
+ });
203
+
204
+ document.addEventListener("click", function (e) {
205
+ if (!extPopover.contains(e.target) && e.target !== extPillBtn && !extPillBtn.contains(e.target)) {
206
+ extPopover.classList.remove("visible");
207
+ }
208
+ });
209
+
210
+ // Download button
211
+ if (extDownloadBtn) {
212
+ extDownloadBtn.addEventListener("click", function (e) {
213
+ e.stopPropagation();
214
+ extDownloadBtn.disabled = true;
215
+ extDownloadBtn.innerHTML = iconHtml("loader") + " Downloading...";
216
+ refreshIcons(extDownloadBtn);
217
+ var loaderIcon = extDownloadBtn.querySelector(".lucide");
218
+ if (loaderIcon) loaderIcon.style.animation = "spin 1s linear infinite";
219
+ if (extDownloadStatus) {
220
+ extDownloadStatus.classList.remove("hidden");
221
+ extDownloadStatus.textContent = "Fetching from GitHub...";
222
+ extDownloadStatus.style.color = "";
223
+ }
224
+ fetch("/api/extension/download").then(function (resp) {
225
+ if (!resp.ok) throw new Error("Download failed (" + resp.status + ")");
226
+ return resp.blob();
227
+ }).then(function (blob) {
228
+ var url = URL.createObjectURL(blob);
229
+ var a = document.createElement("a");
230
+ a.href = url;
231
+ a.download = "clay-chrome-extension.zip";
232
+ document.body.appendChild(a);
233
+ a.click();
234
+ document.body.removeChild(a);
235
+ URL.revokeObjectURL(url);
236
+ if (extDownloadStatus) {
237
+ extDownloadStatus.textContent = "Download complete!";
238
+ extDownloadStatus.style.color = "var(--accent)";
239
+ }
240
+ showToast("Extension downloaded");
241
+ }).catch(function (err) {
242
+ if (extDownloadStatus) {
243
+ extDownloadStatus.textContent = "Failed: " + err.message;
244
+ extDownloadStatus.style.color = "var(--danger, #e53935)";
245
+ }
246
+ showToast("Download failed");
247
+ }).finally(function () {
248
+ extDownloadBtn.disabled = false;
249
+ extDownloadBtn.innerHTML = iconHtml("download") + " Download Extension (.zip)";
250
+ refreshIcons(extDownloadBtn);
251
+ });
252
+ });
253
+ }
254
+
255
+ // Copy chrome://extensions URL
256
+ if (extCopyUrl) {
257
+ extCopyUrl.addEventListener("click", function (e) {
258
+ e.stopPropagation();
259
+ copyToClipboard("chrome://extensions").then(function () {
260
+ showToast("Copied chrome://extensions");
261
+ });
262
+ });
263
+ }
264
+ })();
265
+
158
266
  // --- Settings: Check for updates ---
159
267
  (function () {
160
268
  var settingsUpdateCheck = $("settings-update-check");
package/lib/sdk-bridge.js CHANGED
@@ -134,6 +134,7 @@ function createSDKBridge(opts) {
134
134
  var mateDisplayName = opts.mateDisplayName || "";
135
135
  var isMate = opts.isMate || (slug.indexOf("mate-") === 0);
136
136
  var dangerouslySkipPermissions = opts.dangerouslySkipPermissions || false;
137
+ var mcpServers = opts.mcpServers || null;
137
138
  var onProcessingChanged = opts.onProcessingChanged || function () {};
138
139
  var onTurnDone = opts.onTurnDone || null;
139
140
 
@@ -1128,6 +1129,7 @@ function createSDKBridge(opts) {
1128
1129
  agentProgressSummaries: true,
1129
1130
  };
1130
1131
 
1132
+ if (mcpServers) queryOptions.mcpServers = mcpServers;
1131
1133
  if (sm.currentModel) queryOptions.model = sm.currentModel;
1132
1134
  if (sm.currentEffort) queryOptions.effort = sm.currentEffort;
1133
1135
  if (sm.currentBetas && sm.currentBetas.length > 0) queryOptions.betas = sm.currentBetas;
@@ -1940,6 +1942,7 @@ function createSDKBridge(opts) {
1940
1942
  abortController: session.abortController,
1941
1943
  promptSuggestions: true,
1942
1944
  agentProgressSummaries: true,
1945
+ mcpServers: mcpServers || undefined,
1943
1946
  canUseTool: function(toolName, input, toolOpts) {
1944
1947
  return handleCanUseTool(session, toolName, input, toolOpts);
1945
1948
  },