|
107 | 107 | .hdr-row .row-num{z-index:4;font-weight:600;color:var(--text-sec);cursor:default;background:var(--bg-toolbar)} |
108 | 108 | .flt-row .row-num{z-index:4;cursor:default;background:var(--bg-toolbar)} |
109 | 109 | tr.selected .row-num{background:var(--bg-selected)!important} |
110 | | -.popover{position:fixed;background:var(--pop-bg);border:1px solid var(--border);border-radius:4px;box-shadow:var(--pop-shadow);max-width:420px;min-width:200px;z-index:100;overflow:hidden;opacity:0;transform:translateY(-4px);transition:opacity .15s,transform .15s;pointer-events:none} |
| 110 | +.popover{position:fixed;background:var(--pop-bg);border:1px solid var(--border);border-radius:4px;box-shadow:var(--pop-shadow);max-width:min(720px,90vw);min-width:280px;max-height:min(500px,70vh);z-index:100;overflow:hidden;opacity:0;transform:translateY(-4px);transition:opacity .15s,transform .15s;pointer-events:none;display:flex;flex-direction:column} |
111 | 111 | .popover.visible{opacity:1;transform:translateY(0);pointer-events:auto} |
112 | 112 | .pop-hdr{padding:8px 12px;font-size:10px;font-weight:600;color:var(--text-sec);border-bottom:1px solid var(--border-light);background:var(--bg-alt);text-transform:uppercase;letter-spacing:0.03em} |
113 | | -.pop-body{padding:10px 12px;font-size:11px;line-height:1.5;white-space:pre-wrap;max-height:300px;overflow-y:auto;color:var(--text)} |
| 113 | +.pop-body{padding:10px 12px;font-size:11px;line-height:1.5;white-space:pre-wrap;overflow-y:auto;color:var(--text);flex:1} |
114 | 114 | .toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(60px);background:var(--toast-bg);color:var(--toast-text);padding:6px 16px;border-radius:4px;font-size:11px;font-weight:500;opacity:0;transition:opacity .2s,transform .2s;pointer-events:none;z-index:200} |
115 | 115 | .toast.show{opacity:1;transform:translateX(-50%) translateY(0)} |
116 | 116 | .resize-handle{height:4px;background:var(--border-light);cursor:ns-resize;border-radius:0 0 4px 4px;transition:background .15s;margin-top:-1px;border:1px solid var(--border);border-top:none} |
|
132 | 132 | body.col-resizing,body.col-resizing *{cursor:col-resize!important;user-select:none!important} |
133 | 133 | body.row-resizing,body.row-resizing *{cursor:row-resize!important;user-select:none!important} |
134 | 134 | .cell-text{display:inline} |
135 | | -.cell-more,.cell-less{cursor:pointer;color:var(--accent);font-size:10px;margin-left:2px;white-space:nowrap;font-weight:500} |
136 | | -.cell-more:hover,.cell-less:hover{text-decoration:underline;text-underline-offset:2px} |
| 135 | +.cell-more,.cell-less{cursor:pointer;color:var(--accent);font-size:10px;margin-left:4px;white-space:nowrap;font-weight:500;padding:1px 4px;border-radius:3px;background:rgba(77,79,189,0.08)} |
| 136 | +.cell-more:hover,.cell-less:hover{text-decoration:underline;text-underline-offset:2px;background:rgba(77,79,189,0.15)} |
137 | 137 | .export-btns{display:inline-flex;gap:2px} |
138 | | -.export-btns button{padding:3px 8px;font-size:10px} |
| 138 | +.export-btns a{font-family:inherit} |
139 | 139 | #globalSearch{padding:4px 8px;border:1px solid var(--input-border);border-radius:4px;font-size:11px;background:var(--input-bg);color:var(--text);outline:none;width:160px;transition:border-color .15s ease,width .2s ease;font-family:inherit} |
140 | 140 | #globalSearch:focus{border-color:var(--input-focus);width:220px} |
141 | 141 | #globalSearch::placeholder{color:var(--text-dim)} |
142 | 142 | .col-ghost{position:fixed;background:var(--bg-toolbar);border:1px solid var(--accent);border-radius:4px;padding:4px 8px;font-size:11px;font-weight:600;opacity:.85;pointer-events:none;z-index:200;white-space:nowrap} |
143 | 143 | body.col-dragging,body.col-dragging *{cursor:grabbing!important;user-select:none!important} |
144 | 144 | .hdr-row th.drag-over-left{box-shadow:inset 3px 0 0 var(--accent)} |
145 | 145 | .hdr-row th.drag-over-right{box-shadow:inset -3px 0 0 var(--accent)} |
146 | | -.settings-wrap{position:relative;display:inline-block} |
147 | | -#settingsBtn{font-size:14px;padding:5px 8px} |
148 | | -.settings-drop{position:absolute;top:100%;right:0;margin-top:4px;background:var(--pop-bg);border:1px solid var(--border);border-radius:4px;box-shadow:var(--pop-shadow);padding:8px 0;z-index:100;min-width:130px;display:none} |
149 | | -.settings-drop.show{display:block} |
150 | | -.settings-drop .drop-hdr{padding:2px 12px;font-size:10px;font-weight:600;color:var(--text-sec);text-transform:uppercase;letter-spacing:0.03em} |
151 | | -.settings-drop label{display:flex;align-items:center;gap:6px;padding:4px 12px;font-size:11px;cursor:pointer;white-space:nowrap} |
152 | | -.settings-drop label:hover{background:var(--bg-hover)} |
153 | | -.settings-drop input[type="radio"]{margin:0} |
154 | | -.settings-drop .drop-sep{border-top:1px solid var(--border-light);margin:4px 0} |
155 | 146 | /* ── Widget frame ── */ |
156 | 147 | .widget-frame{border:1px solid var(--border);border-radius:4px;margin:4px;overflow:hidden} |
157 | 148 | </style></head><body> |
|
182 | 173 | <span id="sum">Loading...</span> |
183 | 174 | <button id="selAllBtn">Select all</button> |
184 | 175 | <button id="copyBtn" disabled>Copy CSV (0)</button> |
185 | | - <span class="export-btns"><button id="exportLink" title="Copy CSV download link to clipboard">Copy link</button></span> |
186 | | - <span class="settings-wrap"><button id="settingsBtn" title="Settings">Settings</button><div id="settingsDrop" class="settings-drop"><div class="drop-hdr">Copy format</div><label><input type="radio" name="cfmt" value="csv" checked> CSV</label><label><input type="radio" name="cfmt" value="tsv"> TSV (tabs)</label><label><input type="radio" name="cfmt" value="json"> JSON</label></div></span> |
| 176 | + <span class="export-btns"><button id="exportLink" title="Copy CSV download link to clipboard">Download CSV</button></span> |
187 | 177 | </div> |
188 | 178 | <div class="wrap" id="wrap" style="max-height:520px"><table id="tbl"></table></div> |
189 | 179 | <div class="resize-handle" id="resizeHandle"></div> |
|
226 | 216 | let sessionUrl="",csvUrl="",pollToken="",downloadUrl=""; |
227 | 217 | const TRUNC=200; |
228 | 218 | let didDrag=false; |
229 | | -let copyFmt="csv"; |
| 219 | +const copyFmt="csv"; |
230 | 220 | let widgetActive=false; |
231 | | -const settingsBtn=document.getElementById("settingsBtn"); |
232 | | -const settingsDrop=document.getElementById("settingsDrop"); |
233 | 221 | const S={rows:[],allCols:[],filteredIdx:[],sortCol:null,sortDir:0,filters:{},globalQuery:"",selected:new Set(),lastClick:null,isFullscreen:false,focusedCell:null}; |
234 | 222 |
|
235 | 223 | /* ── progress state ── */ |
236 | 224 | let pollUrl=null,pollTimer=null,wasDone=false,pollCursor=null; |
237 | | -let progressMode=false,resultsFetched=false; |
| 225 | +let progressMode=false,resultsFetched=false,notifiedClaude=false; |
| 226 | +let currentTaskId=null; |
238 | 227 | const aggHistory=[]; /* [{aggregate,micros:[{text,row_index}],ts}] */ |
239 | 228 | let activeTab="activity"; |
240 | 229 |
|
|
382 | 371 | progressSection.classList.add("flash"); |
383 | 372 | /* auto-fetch results on completion */ |
384 | 373 | if(!resultsFetched)autoFetchResults(); |
| 374 | + /* notify Claude so it can present results in the conversation */ |
| 375 | + notifyClaude(d); |
385 | 376 | } |
386 | 377 | if(done&&pollTimer){clearInterval(pollTimer);pollTimer=null;} |
387 | 378 | } |
388 | 379 |
|
389 | 380 |
|
| 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 | +} |
| 389 | +
|
390 | 390 | /* ── auto-fetch results on completion ── */ |
391 | 391 | async function autoFetchResults(){ |
392 | 392 | if(resultsFetched)return; |
|
430 | 430 | activityTab.style.display="block"; |
431 | 431 | resultsTab.style.display="none"; |
432 | 432 | sessionUrl=d.session_url||sessionUrl; |
| 433 | + if(d.task_id)currentTaskId=d.task_id; |
| 434 | + /* Extract task_id from progress_url as fallback */ |
| 435 | + if(!currentTaskId&&d.progress_url){const m=d.progress_url.match(/progress\\/([0-9a-f-]+)/);if(m)currentTaskId=m[1];} |
433 | 436 | if(d.poll_token)pollToken=d.poll_token; |
434 | 437 | if(d.download_url)downloadUrl=d.download_url; |
435 | 438 | renderProgress(d); |
|
707 | 710 | if(!S.selected.size)return; |
708 | 711 | const text=buildCopyText(); |
709 | 712 | const msg="Copied "+S.selected.size+" row"+(S.selected.size>1?"s":"")+" as "+copyFmt.toUpperCase(); |
| 713 | + /* Clipboard API often fails in sandboxed iframes — try it first, |
| 714 | + fall back to execCommand, then show modal for manual copy. */ |
710 | 715 | try{await navigator.clipboard.writeText(text);showToast(msg);return;}catch{} |
711 | | - if(execCopy(text)){showToast(msg);return;} |
| 716 | + try{if(execCopy(text)){showToast(msg);return;}}catch{} |
712 | 717 | showCopyModal(text); |
713 | 718 | }); |
714 | 719 | closeCopyModal.addEventListener("click",()=>copyModal.classList.remove("show")); |
715 | 720 | copyModal.addEventListener("click",e=>{if(e.target===copyModal)copyModal.classList.remove("show");}); |
716 | 721 | function showToast(msg){toast.textContent=msg;toast.classList.add("show");setTimeout(()=>toast.classList.remove("show"),2000);} |
717 | 722 |
|
718 | | -/* --- settings dropdown --- */ |
719 | | -settingsBtn.addEventListener("click",e=>{e.stopPropagation();settingsDrop.classList.toggle("show");}); |
720 | | -document.addEventListener("click",()=>settingsDrop.classList.remove("show")); |
721 | | -settingsDrop.addEventListener("click",e=>e.stopPropagation()); |
722 | | -settingsDrop.querySelectorAll('input[name="cfmt"]').forEach(r=>{ |
723 | | - r.addEventListener("change",()=>{copyFmt=r.value;updateCopyBtn();}); |
724 | | -}); |
725 | 723 |
|
726 | 724 | /* --- popover --- */ |
727 | 725 | let popTimer=null,popTarget=null,popVisible=false; |
|
938 | 936 | document.getElementById("exportLink")?.addEventListener("click",()=>{ |
939 | 937 | const url=getDownloadUrl(); |
940 | 938 | if(!url){showToast("No download link yet");return;} |
941 | | - copyToClipboard(url).then(ok=>{if(ok)showToast("Link copied");}); |
| 939 | + app.openLink({url}).catch(()=>showCopyModal(url)); |
942 | 940 | }); |
943 | 941 |
|
944 | 942 | /* --- row resize (drag bottom border) --- */ |
|
0 commit comments