Skip to content

Commit 556c3ad

Browse files
RafaelPogithub-actions[bot]
authored andcommitted
fix: widget completion popup, popover overlap, status prompt update (#5081)
## Summary Three widget UX improvements: 1. **Completion popup** — on task finish, a green dismissable banner slides in: "Task complete — ask Claude to get the results." Replaces the `sendMessage` composer prefill (which was confusing). User clicks × to dismiss. 2. **Popover stays open** — cell mouseover popover now overlaps the cell by 8px and has a 400ms hide delay (was 4px gap + 150ms). Cursor can move from cell to popover without the popover disappearing. 3. **Status prompt update** — after `futuresearch_status`, Claude tells the user: "The widget above is tracking progress. When it's done, it will let you know — just ask me to get the results when you're ready." This sets correct expectations since Claude has no way to know when the task completes. ## Test plan - [x] Tested in Claude.ai via CF tunnel - [x] Banner appears on task completion, dismissable with × - [x] Popover stays open when moving cursor from cell to popover - [x] Claude explains it won't know when task finishes 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Sourced from commit 0d808fc631ef737e675aab9a2363296b6a053496
1 parent d394f0f commit 556c3ad

2 files changed

Lines changed: 16 additions & 15 deletions

File tree

futuresearch-mcp/src/futuresearch_mcp/templates.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@
124124
.copy-modal-box textarea{width:100%;height:300px;font-family:inherit;font-size:11px;border:1px solid var(--border);border-radius:4px;padding:8px;background:var(--input-bg);color:var(--text);resize:vertical}
125125
.copy-modal-box .modal-btns{display:flex;gap:8px;justify-content:flex-end}
126126
.copy-modal-box button{padding:6px 16px;border:1px solid var(--border);border-radius:4px;background:var(--btn-bg);color:var(--btn-text);cursor:pointer;font-size:11px;font-family:inherit}
127+
.done-banner{position:fixed;top:0;left:0;right:0;background:var(--seg-done);color:#fff;padding:10px 16px;z-index:250;display:flex;align-items:center;gap:10px;font-size:12px;font-weight:500;box-shadow:0 2px 8px rgba(0,0,0,.15);transform:translateY(-100%);transition:transform .3s ease}
128+
.done-banner.show{transform:translateY(0)}
129+
.done-banner .banner-text{flex:1}
130+
.done-banner .banner-close{background:none;border:none;color:#fff;font-size:18px;cursor:pointer;padding:0 4px;line-height:1;opacity:.8}
131+
.done-banner .banner-close:hover{opacity:1}
127132
.session-link{margin-bottom:6px;font-size:11px;display:flex;align-items:center;gap:8px}
128133
.session-link a{font-weight:500}
129134
.session-link .spacer{flex:1}
@@ -181,6 +186,7 @@
181186
</div><!-- close widget-frame -->
182187
183188
<div id="pop" class="popover"><div class="pop-hdr"></div><div class="pop-body"></div></div>
189+
<div id="doneBanner" class="done-banner"><span class="banner-text">Task complete — ask Claude to get the results.</span><button class="banner-close" id="closeBanner">&times;</button></div>
184190
<div id="toast" class="toast">Copied!</div>
185191
<div id="copyModal" class="copy-modal"><div class="copy-modal-box"><div style="font-weight:600;font-size:13px">Select all and copy (Cmd+C / Ctrl+C)</div><textarea id="copyArea" readonly></textarea><div class="modal-btns"><button id="closeCopyModal">Close</button></div></div></div>
186192
<script type="module">
@@ -222,7 +228,7 @@
222228
223229
/* ── progress state ── */
224230
let pollUrl=null,pollTimer=null,wasDone=false,pollCursor=null;
225-
let progressMode=false,resultsFetched=false,notifiedClaude=false;
231+
let progressMode=false,resultsFetched=false;
226232
let currentTaskId=null;
227233
const aggHistory=[]; /* [{aggregate,micros:[{text,row_index}],ts}] */
228234
let activeTab="activity";
@@ -371,21 +377,16 @@
371377
progressSection.classList.add("flash");
372378
/* auto-fetch results on completion */
373379
if(!resultsFetched)autoFetchResults();
374-
/* notify Claude so it can present results in the conversation */
375-
notifyClaude(d);
380+
showDoneBanner();
376381
}
377382
if(done&&pollTimer){clearInterval(pollTimer);pollTimer=null;}
378383
}
379384
380385
381-
/* ── notify Claude on completion so it can present results ── */
382-
async function notifyClaude(d){
383-
if(notifiedClaude||!currentTaskId)return;
384-
notifiedClaude=true;
385-
try{
386-
await app.sendMessage({role:"user",content:[{type:"text",text:"The task is now done. Get the results."}]});
387-
}catch(e){console.error("[notify] sendMessage failed:",e);}
388-
}
386+
/* ── show completion banner ── */
387+
const doneBanner=document.getElementById("doneBanner");
388+
document.getElementById("closeBanner").addEventListener("click",()=>doneBanner.classList.remove("show"));
389+
function showDoneBanner(){doneBanner.classList.add("show");}
389390
390391
/* ── auto-fetch results on completion ── */
391392
async function autoFetchResults(){
@@ -731,12 +732,12 @@
731732
popHdr.textContent="research."+col.replace(/^research\\./,"");
732733
popBody.innerHTML=linkify(text);
733734
const rect=td.getBoundingClientRect();
734-
let left=rect.left,top=rect.bottom+4;
735+
let left=rect.left,top=rect.bottom-8;
735736
pop.classList.add("visible");popVisible=true;
736737
const pw=pop.offsetWidth,ph=pop.offsetHeight;
737738
if(left+pw>window.innerWidth-8)left=window.innerWidth-pw-8;
738739
if(left<8)left=8;
739-
if(top+ph>window.innerHeight-8)top=rect.top-ph-4;
740+
if(top+ph>window.innerHeight-8)top=rect.top-ph+8;
740741
pop.style.left=left+"px";pop.style.top=top+"px";
741742
}
742743
function hidePopover(){pop.classList.remove("visible");popVisible=false;popTarget=null;}
@@ -750,7 +751,7 @@
750751
popTarget=td;popTimer=setTimeout(()=>showPopover(td),300);
751752
}else{
752753
clearTimeout(popTimer);popTarget=null;
753-
if(popVisible)popTimer=setTimeout(()=>{if(!pop.matches(":hover"))hidePopover();},150);
754+
if(popVisible)popTimer=setTimeout(()=>{if(!pop.matches(":hover"))hidePopover();},400);
754755
}
755756
});
756757
pop.addEventListener("mouseleave",()=>{clearTimeout(popTimer);hidePopover();});

futuresearch-mcp/src/futuresearch_mcp/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ async def futuresearch_status(
10921092
- Do NOT proactively call futuresearch_results — the widget loads results automatically when the task completes. Only call it if the user asks to see or discuss the results.
10931093
- Do NOT call futuresearch_progress — the widget polls automatically.
10941094
- Do NOT call futuresearch_status again.
1095-
- Wait for the user to tell you what to do next.
1095+
- You will NOT be notified when the task completes. The widget will notify the user directly. Tell the user: "The widget above is tracking progress. When it's done, it will let you know — just ask me to get the results when you're ready."
10961096
""")
10971097

10981098
return CallToolResult(

0 commit comments

Comments
 (0)