clay-server 2.26.0-beta.4 → 2.26.0-beta.6
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/browser-mcp-server.js +496 -0
- package/lib/project-mate-interaction.js +760 -0
- package/lib/project-memory.js +677 -0
- package/lib/project.js +381 -1364
- package/lib/public/app.js +289 -2
- package/lib/public/css/input.css +16 -0
- package/lib/public/css/overlays.css +181 -0
- package/lib/public/css/rewind.css +79 -0
- package/lib/public/css/server-settings.css +1 -0
- package/lib/public/css/title-bar.css +189 -3
- package/lib/public/index.html +23 -0
- package/lib/public/modules/context-sources.js +116 -29
- package/lib/public/modules/notifications.js +109 -1
- package/lib/sdk-bridge.js +22 -0
- package/lib/sdk-worker.js +13 -1
- package/lib/server.js +42 -0
- package/lib/sessions.js +16 -1
- package/package.json +2 -2
package/lib/public/app.js
CHANGED
|
@@ -12,7 +12,7 @@ import { initInput, clearPendingImages, handleInputSync, autoResize, builtinComm
|
|
|
12
12
|
import { initQrCode, triggerShare } from './modules/qrcode.js';
|
|
13
13
|
import { initFileBrowser, loadRootDirectory, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer, resetFileBrowser } from './modules/filebrowser.js';
|
|
14
14
|
import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed, sendTerminalCommand } from './modules/terminal.js';
|
|
15
|
-
import { initContextSources, updateTerminalList, handleContextSourcesState, getActiveSources, hasActiveSources } from './modules/context-sources.js';
|
|
15
|
+
import { initContextSources, updateTerminalList, updateBrowserTabList, handleContextSourcesState, getActiveSources, hasActiveSources } from './modules/context-sources.js';
|
|
16
16
|
import { initStickyNotes, handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, openArchive, closeArchive, isArchiveOpen, hideNotes, showNotes, isNotesVisible } from './modules/sticky-notes.js';
|
|
17
17
|
import { initTheme, getThemeColor, getComputedVar, onThemeChange, getCurrentTheme } from './modules/theme.js';
|
|
18
18
|
import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderElicitationRequest, markElicitationResolved, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, resetTurnMetaCost, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, updateSubagentProgress, initSubagentStop, closeToolGroup, removeToolFromGroup } from './modules/tools.js';
|
|
@@ -2599,6 +2599,9 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
2599
2599
|
var contextMiniLabel = $("context-mini-label");
|
|
2600
2600
|
var contextData = { contextWindow: 0, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0 };
|
|
2601
2601
|
var headerContextEl = null;
|
|
2602
|
+
var richContextUsage = null;
|
|
2603
|
+
var ctxPopoverEl = null;
|
|
2604
|
+
var ctxPopoverVisible = false;
|
|
2602
2605
|
|
|
2603
2606
|
// Known context window sizes per model (fallback when SDK omits feature flag)
|
|
2604
2607
|
var KNOWN_CONTEXT_WINDOWS = {
|
|
@@ -2646,6 +2649,14 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
2646
2649
|
headerContextEl.className = "header-context";
|
|
2647
2650
|
headerContextEl.innerHTML = '<div class="header-context-bar"><div class="header-context-fill"></div></div><span class="header-context-label"></span>';
|
|
2648
2651
|
statusArea.insertBefore(headerContextEl, statusArea.firstChild);
|
|
2652
|
+
headerContextEl.addEventListener("mouseenter", function() {
|
|
2653
|
+
if (richContextUsage) {
|
|
2654
|
+
showCtxPopover();
|
|
2655
|
+
}
|
|
2656
|
+
});
|
|
2657
|
+
headerContextEl.addEventListener("mouseleave", function() {
|
|
2658
|
+
ctxHoverTimer = setTimeout(hideCtxPopover, 120);
|
|
2659
|
+
});
|
|
2649
2660
|
}
|
|
2650
2661
|
if (headerContextEl) {
|
|
2651
2662
|
var hFill = headerContextEl.querySelector(".header-context-fill");
|
|
@@ -2653,7 +2664,12 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
2653
2664
|
hFill.style.width = pct.toFixed(1) + "%";
|
|
2654
2665
|
hFill.className = "header-context-fill" + cls;
|
|
2655
2666
|
hLabel.textContent = pct.toFixed(0) + "%";
|
|
2656
|
-
|
|
2667
|
+
// Use data-tip as fallback when rich data is not yet loaded
|
|
2668
|
+
if (richContextUsage) {
|
|
2669
|
+
headerContextEl.removeAttribute("data-tip");
|
|
2670
|
+
} else {
|
|
2671
|
+
headerContextEl.dataset.tip = "Context window " + pct.toFixed(0) + "% used (" + formatTokens(used) + " / " + formatTokens(win) + " tokens)";
|
|
2672
|
+
}
|
|
2657
2673
|
}
|
|
2658
2674
|
}
|
|
2659
2675
|
contextUsedEl.textContent = formatTokens(used);
|
|
@@ -2718,9 +2734,158 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
2718
2734
|
|
|
2719
2735
|
function resetContextData() {
|
|
2720
2736
|
contextData = { contextWindow: 0, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0 };
|
|
2737
|
+
richContextUsage = null;
|
|
2738
|
+
hideCtxPopover();
|
|
2721
2739
|
updateContextPanel();
|
|
2722
2740
|
}
|
|
2723
2741
|
|
|
2742
|
+
// --- Rich context usage popover ---
|
|
2743
|
+
|
|
2744
|
+
var ctxHoverTimer = null;
|
|
2745
|
+
|
|
2746
|
+
function ensureCtxPopover() {
|
|
2747
|
+
if (ctxPopoverEl) return;
|
|
2748
|
+
ctxPopoverEl = document.createElement("div");
|
|
2749
|
+
ctxPopoverEl.className = "context-usage-popover hidden";
|
|
2750
|
+
// Keep popover open when hovering over it
|
|
2751
|
+
ctxPopoverEl.addEventListener("mouseenter", function() {
|
|
2752
|
+
if (ctxHoverTimer) { clearTimeout(ctxHoverTimer); ctxHoverTimer = null; }
|
|
2753
|
+
});
|
|
2754
|
+
ctxPopoverEl.addEventListener("mouseleave", function() {
|
|
2755
|
+
hideCtxPopover();
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
function showCtxPopover() {
|
|
2760
|
+
if (!headerContextEl || !richContextUsage) return;
|
|
2761
|
+
if (ctxHoverTimer) { clearTimeout(ctxHoverTimer); ctxHoverTimer = null; }
|
|
2762
|
+
ensureCtxPopover();
|
|
2763
|
+
headerContextEl.appendChild(ctxPopoverEl);
|
|
2764
|
+
renderCtxPopover();
|
|
2765
|
+
ctxPopoverEl.classList.remove("hidden");
|
|
2766
|
+
ctxPopoverVisible = true;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
function hideCtxPopover() {
|
|
2770
|
+
if (!ctxPopoverEl) return;
|
|
2771
|
+
ctxPopoverEl.classList.add("hidden");
|
|
2772
|
+
ctxPopoverVisible = false;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
// Categories to hide from the legend (noise, not actionable)
|
|
2776
|
+
var CTX_HIDDEN_CATS = { "Free space": 1, "Autocompact buffer": 1 };
|
|
2777
|
+
|
|
2778
|
+
function renderCtxPopover() {
|
|
2779
|
+
if (!ctxPopoverEl || !richContextUsage) return;
|
|
2780
|
+
var d = richContextUsage;
|
|
2781
|
+
var cats = d.categories || [];
|
|
2782
|
+
var total = d.totalTokens || 0;
|
|
2783
|
+
var max = d.maxTokens || 0;
|
|
2784
|
+
var pct = d.percentage != null ? d.percentage : (max > 0 ? (total / max) * 100 : 0);
|
|
2785
|
+
|
|
2786
|
+
var html = "";
|
|
2787
|
+
|
|
2788
|
+
// Header
|
|
2789
|
+
html += '<div class="ctx-pop-header">';
|
|
2790
|
+
html += '<span class="ctx-pop-model">' + escHtml(d.model || contextData.model || "-") + '</span>';
|
|
2791
|
+
html += '<span class="ctx-pop-pct">' + pct.toFixed(0) + '%';
|
|
2792
|
+
html += '<span class="ctx-pop-tokens">' + formatTokens(total) + ' / ' + formatTokens(max) + '</span>';
|
|
2793
|
+
html += '</span>';
|
|
2794
|
+
html += '</div>';
|
|
2795
|
+
|
|
2796
|
+
// Category emoji map
|
|
2797
|
+
var CTX_EMOJI = {
|
|
2798
|
+
"System prompt": "\ud83d\udcdc", "System tools": "\ud83d\udee0\ufe0f",
|
|
2799
|
+
"Memory files": "\ud83d\udcc1", "Skills": "\u26a1", "Messages": "\ud83d\udcac",
|
|
2800
|
+
"MCP tools": "\ud83d\udd0c", "Agents": "\ud83e\udd16", "Deferred tools": "\ud83d\udce6"
|
|
2801
|
+
};
|
|
2802
|
+
|
|
2803
|
+
// Stacked bar
|
|
2804
|
+
if (cats.length > 0 && max > 0) {
|
|
2805
|
+
html += '<div class="ctx-cat-bar">';
|
|
2806
|
+
for (var i = 0; i < cats.length; i++) {
|
|
2807
|
+
var cat = cats[i];
|
|
2808
|
+
if (cat.isDeferred || !cat.tokens || CTX_HIDDEN_CATS[cat.name]) continue;
|
|
2809
|
+
var w = Math.max(0.3, (cat.tokens / max) * 100);
|
|
2810
|
+
html += '<div style="width:' + w.toFixed(2) + '%;background:' + escHtml(cat.color) + '"></div>';
|
|
2811
|
+
}
|
|
2812
|
+
html += '</div>';
|
|
2813
|
+
|
|
2814
|
+
// Legend
|
|
2815
|
+
html += '<div class="ctx-cat-legend">';
|
|
2816
|
+
for (var j = 0; j < cats.length; j++) {
|
|
2817
|
+
var c = cats[j];
|
|
2818
|
+
if (c.isDeferred || !c.tokens || CTX_HIDDEN_CATS[c.name]) continue;
|
|
2819
|
+
var emoji = CTX_EMOJI[c.name] || "\ud83d\udcca";
|
|
2820
|
+
html += '<div class="ctx-cat-item">';
|
|
2821
|
+
html += '<span class="ctx-cat-name">' + em(emoji) + ' ' + escHtml(c.name) + '</span>';
|
|
2822
|
+
html += '<span class="ctx-cat-value">' + formatTokens(c.tokens) + '</span>';
|
|
2823
|
+
html += '</div>';
|
|
2824
|
+
}
|
|
2825
|
+
html += '</div>';
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
// Message breakdown
|
|
2829
|
+
var mb = d.messageBreakdown;
|
|
2830
|
+
if (mb) {
|
|
2831
|
+
html += '<div class="ctx-pop-divider"></div>';
|
|
2832
|
+
html += '<div class="ctx-pop-section-label">' + em("\ud83d\udcac") + ' Messages</div>';
|
|
2833
|
+
if (mb.userMessageTokens) {
|
|
2834
|
+
html += '<div class="ctx-pop-row"><span class="ctx-pop-row-label">' + em("\ud83d\udc64") + ' User</span><span class="ctx-pop-row-value">' + formatTokens(mb.userMessageTokens) + '</span></div>';
|
|
2835
|
+
}
|
|
2836
|
+
if (mb.assistantMessageTokens) {
|
|
2837
|
+
html += '<div class="ctx-pop-row"><span class="ctx-pop-row-label">' + em("\ud83e\udd16") + ' Assistant</span><span class="ctx-pop-row-value">' + formatTokens(mb.assistantMessageTokens) + '</span></div>';
|
|
2838
|
+
}
|
|
2839
|
+
if (mb.toolCallTokens) {
|
|
2840
|
+
html += '<div class="ctx-pop-row"><span class="ctx-pop-row-label">' + em("\ud83d\udee0\ufe0f") + ' Tool calls</span><span class="ctx-pop-row-value">' + formatTokens(mb.toolCallTokens) + '</span></div>';
|
|
2841
|
+
}
|
|
2842
|
+
if (mb.toolResultTokens) {
|
|
2843
|
+
html += '<div class="ctx-pop-row"><span class="ctx-pop-row-label">' + em("\ud83d\udccb") + ' Tool results</span><span class="ctx-pop-row-value">' + formatTokens(mb.toolResultTokens) + '</span></div>';
|
|
2844
|
+
}
|
|
2845
|
+
if (mb.attachmentTokens) {
|
|
2846
|
+
html += '<div class="ctx-pop-row"><span class="ctx-pop-row-label">' + em("\ud83d\udcce") + ' Attachments</span><span class="ctx-pop-row-value">' + formatTokens(mb.attachmentTokens) + '</span></div>';
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
// Memory files
|
|
2851
|
+
var mf = d.memoryFiles;
|
|
2852
|
+
if (mf && mf.length > 0) {
|
|
2853
|
+
html += '<div class="ctx-pop-divider"></div>';
|
|
2854
|
+
html += '<div class="ctx-pop-section-label">' + em("\ud83d\udcc1") + ' Memory Files</div>';
|
|
2855
|
+
var baseCount = {};
|
|
2856
|
+
for (var mc = 0; mc < mf.length; mc++) {
|
|
2857
|
+
var bn = mf[mc].path.split("/").pop() || mf[mc].path;
|
|
2858
|
+
baseCount[bn] = (baseCount[bn] || 0) + 1;
|
|
2859
|
+
}
|
|
2860
|
+
for (var mi = 0; mi < mf.length; mi++) {
|
|
2861
|
+
var fpath = mf[mi].path;
|
|
2862
|
+
var fname = fpath.split("/").pop() || fpath;
|
|
2863
|
+
if (baseCount[fname] > 1) {
|
|
2864
|
+
var parts = fpath.split("/");
|
|
2865
|
+
fname = parts.length >= 2 ? parts[parts.length - 2] + "/" + fname : fpath;
|
|
2866
|
+
}
|
|
2867
|
+
html += '<div class="ctx-pop-row"><span class="ctx-pop-row-label">' + em("\ud83d\udcc4") + ' ' + escHtml(fname) + '</span><span class="ctx-pop-row-value">' + formatTokens(mf[mi].tokens) + '</span></div>';
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
// Auto-compact note
|
|
2872
|
+
if (d.isAutoCompactEnabled && d.autoCompactThreshold) {
|
|
2873
|
+
html += '<div class="ctx-pop-note">' + em("\u267b\ufe0f") + ' Auto-compact at ' + formatTokens(d.autoCompactThreshold) + '</div>';
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
ctxPopoverEl.innerHTML = html;
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
function escHtml(s) {
|
|
2880
|
+
var div = document.createElement("div");
|
|
2881
|
+
div.textContent = s;
|
|
2882
|
+
return div.innerHTML;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
function em(emoji) {
|
|
2886
|
+
return '<span class="ctx-emoji">' + emoji + '</span>';
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2724
2889
|
function resetContext() {
|
|
2725
2890
|
resetContextData();
|
|
2726
2891
|
// Keep view state, just reset data
|
|
@@ -4052,6 +4217,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4052
4217
|
|
|
4053
4218
|
case "history_done":
|
|
4054
4219
|
replayingHistory = false;
|
|
4220
|
+
// Restore cached rich context usage BEFORE updateContextPanel runs
|
|
4221
|
+
if (msg.contextUsage) {
|
|
4222
|
+
richContextUsage = msg.contextUsage;
|
|
4223
|
+
}
|
|
4055
4224
|
// Restore accurate context data from the last result in full history
|
|
4056
4225
|
if (msg.lastUsage || msg.lastModelUsage) {
|
|
4057
4226
|
accumulateContext(msg.lastCost, msg.lastUsage, msg.lastModelUsage, msg.lastStreamInputTokens);
|
|
@@ -4419,6 +4588,59 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4419
4588
|
}
|
|
4420
4589
|
break;
|
|
4421
4590
|
|
|
4591
|
+
case "context_preview":
|
|
4592
|
+
// Show a Context Card with tab screenshot between user message and assistant response
|
|
4593
|
+
if (msg.tab) {
|
|
4594
|
+
var card = document.createElement("div");
|
|
4595
|
+
card.className = "context-card";
|
|
4596
|
+
|
|
4597
|
+
// Header
|
|
4598
|
+
var header = document.createElement("div");
|
|
4599
|
+
header.className = "context-card-header";
|
|
4600
|
+
var icon = document.createElement("span");
|
|
4601
|
+
icon.className = "context-card-icon";
|
|
4602
|
+
icon.textContent = "\uD83D\uDC41";
|
|
4603
|
+
header.appendChild(icon);
|
|
4604
|
+
var label = document.createElement("span");
|
|
4605
|
+
label.textContent = "Viewing tab";
|
|
4606
|
+
header.appendChild(label);
|
|
4607
|
+
card.appendChild(header);
|
|
4608
|
+
|
|
4609
|
+
// Screenshot
|
|
4610
|
+
if (msg.tab.screenshotUrl) {
|
|
4611
|
+
var img = document.createElement("img");
|
|
4612
|
+
img.className = "context-card-screenshot";
|
|
4613
|
+
img.src = msg.tab.screenshotUrl;
|
|
4614
|
+
img.loading = "lazy";
|
|
4615
|
+
img.addEventListener("click", function () { showImageModal(this.src); });
|
|
4616
|
+
card.appendChild(img);
|
|
4617
|
+
}
|
|
4618
|
+
|
|
4619
|
+
// Meta: title + domain
|
|
4620
|
+
var tabTitle = msg.tab.title || "";
|
|
4621
|
+
var tabDomain = "";
|
|
4622
|
+
try { tabDomain = new URL(msg.tab.url).hostname; } catch (e) {}
|
|
4623
|
+
if (tabTitle || tabDomain) {
|
|
4624
|
+
var meta = document.createElement("div");
|
|
4625
|
+
meta.className = "context-card-meta";
|
|
4626
|
+
var titleEl = document.createElement("span");
|
|
4627
|
+
titleEl.className = "context-card-title";
|
|
4628
|
+
titleEl.textContent = tabTitle;
|
|
4629
|
+
meta.appendChild(titleEl);
|
|
4630
|
+
if (tabDomain) {
|
|
4631
|
+
var domainEl = document.createElement("span");
|
|
4632
|
+
domainEl.className = "context-card-domain";
|
|
4633
|
+
domainEl.textContent = tabDomain;
|
|
4634
|
+
meta.appendChild(domainEl);
|
|
4635
|
+
}
|
|
4636
|
+
card.appendChild(meta);
|
|
4637
|
+
}
|
|
4638
|
+
|
|
4639
|
+
messagesEl.appendChild(card);
|
|
4640
|
+
scrollToBottom();
|
|
4641
|
+
}
|
|
4642
|
+
break;
|
|
4643
|
+
|
|
4422
4644
|
case "status":
|
|
4423
4645
|
if (msg.status === "processing") {
|
|
4424
4646
|
setStatus("processing");
|
|
@@ -4614,6 +4836,14 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4614
4836
|
accumulateContext(msg.cost, msg.usage, msg.modelUsage, msg.lastStreamInputTokens);
|
|
4615
4837
|
break;
|
|
4616
4838
|
|
|
4839
|
+
case "context_usage":
|
|
4840
|
+
if (msg.data && !replayingHistory) {
|
|
4841
|
+
richContextUsage = msg.data;
|
|
4842
|
+
if (headerContextEl) headerContextEl.removeAttribute("data-tip");
|
|
4843
|
+
if (ctxPopoverVisible) renderCtxPopover();
|
|
4844
|
+
}
|
|
4845
|
+
break;
|
|
4846
|
+
|
|
4617
4847
|
case "done":
|
|
4618
4848
|
setActivity(null);
|
|
4619
4849
|
stopThinking();
|
|
@@ -4795,6 +5025,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
4795
5025
|
handleContextSourcesState(msg);
|
|
4796
5026
|
break;
|
|
4797
5027
|
|
|
5028
|
+
case "extension_command":
|
|
5029
|
+
sendExtensionCommand(msg.command, msg.args, msg.requestId);
|
|
5030
|
+
break;
|
|
5031
|
+
|
|
4798
5032
|
case "term_created":
|
|
4799
5033
|
handleTermCreated(msg);
|
|
4800
5034
|
if (pendingTermCommand) {
|
|
@@ -5765,6 +5999,59 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
5765
5999
|
get connected() { return connected; },
|
|
5766
6000
|
});
|
|
5767
6001
|
|
|
6002
|
+
// --- Chrome Extension Bridge ---
|
|
6003
|
+
var _extRequestCallbacks = {}; // requestId -> callback function
|
|
6004
|
+
|
|
6005
|
+
function sendExtensionCommand(command, args, requestId) {
|
|
6006
|
+
window.postMessage({
|
|
6007
|
+
source: "clay-page",
|
|
6008
|
+
payload: {
|
|
6009
|
+
type: "clay_ext_command",
|
|
6010
|
+
command: command,
|
|
6011
|
+
args: args,
|
|
6012
|
+
requestId: requestId
|
|
6013
|
+
}
|
|
6014
|
+
}, "*");
|
|
6015
|
+
}
|
|
6016
|
+
|
|
6017
|
+
function handleExtensionResult(requestId, result) {
|
|
6018
|
+
// Check local callback first (for server-initiated requests)
|
|
6019
|
+
var cb = _extRequestCallbacks[requestId];
|
|
6020
|
+
if (cb) {
|
|
6021
|
+
delete _extRequestCallbacks[requestId];
|
|
6022
|
+
cb(result);
|
|
6023
|
+
return;
|
|
6024
|
+
}
|
|
6025
|
+
// Forward to server
|
|
6026
|
+
if (ws && ws.readyState === 1) {
|
|
6027
|
+
ws.send(JSON.stringify({
|
|
6028
|
+
type: "extension_result",
|
|
6029
|
+
requestId: requestId,
|
|
6030
|
+
result: result
|
|
6031
|
+
}));
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
|
|
6035
|
+
window.addEventListener("message", function(event) {
|
|
6036
|
+
if (event.source !== window) return;
|
|
6037
|
+
if (!event.data || event.data.source !== "clay-chrome-extension") return;
|
|
6038
|
+
var msg = event.data.payload;
|
|
6039
|
+
|
|
6040
|
+
if (msg.type === "clay_ext_tab_list") {
|
|
6041
|
+
updateBrowserTabList(msg.tabs);
|
|
6042
|
+
// Also inform server about tab list
|
|
6043
|
+
if (ws && ws.readyState === 1) {
|
|
6044
|
+
ws.send(JSON.stringify({
|
|
6045
|
+
type: "browser_tab_list",
|
|
6046
|
+
tabs: msg.tabs
|
|
6047
|
+
}));
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
if (msg.type === "clay_ext_result") {
|
|
6051
|
+
handleExtensionResult(msg.requestId, msg.result);
|
|
6052
|
+
}
|
|
6053
|
+
});
|
|
6054
|
+
|
|
5768
6055
|
// --- Playbook Engine ---
|
|
5769
6056
|
initPlaybook();
|
|
5770
6057
|
|
package/lib/public/css/input.css
CHANGED
|
@@ -328,6 +328,12 @@
|
|
|
328
328
|
white-space: nowrap;
|
|
329
329
|
border: 1px solid var(--border);
|
|
330
330
|
transition: border-color 0.15s;
|
|
331
|
+
animation: chipIn 0.3s ease-out;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@keyframes chipIn {
|
|
335
|
+
from { opacity: 0; transform: translateY(6px) scale(0.95); }
|
|
336
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
331
337
|
}
|
|
332
338
|
|
|
333
339
|
.context-chip-label {
|
|
@@ -368,6 +374,8 @@
|
|
|
368
374
|
bottom: calc(100% + 4px);
|
|
369
375
|
left: 0;
|
|
370
376
|
min-width: 200px;
|
|
377
|
+
max-height: 320px;
|
|
378
|
+
overflow-y: auto;
|
|
371
379
|
background: var(--sidebar-bg);
|
|
372
380
|
border: 1px solid var(--border);
|
|
373
381
|
border-radius: 10px;
|
|
@@ -430,6 +438,14 @@
|
|
|
430
438
|
text-align: center;
|
|
431
439
|
}
|
|
432
440
|
|
|
441
|
+
.context-picker-favicon {
|
|
442
|
+
width: 14px;
|
|
443
|
+
height: 14px;
|
|
444
|
+
border-radius: 2px;
|
|
445
|
+
flex-shrink: 0;
|
|
446
|
+
object-fit: contain;
|
|
447
|
+
}
|
|
448
|
+
|
|
433
449
|
/* ==========================================================================
|
|
434
450
|
Input Area — Claude-style unified container
|
|
435
451
|
========================================================================== */
|
|
@@ -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
|
========================================================================== */
|