@qiaolei81/copilot-session-viewer 0.3.6 → 0.3.8

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.
@@ -1,5 +1,5 @@
1
1
  (()=>{var{createApp:st,ref:h,computed:v,onMounted:Ce,onUnmounted:at}=Vue,Me=st({setup(){let C=h(window.__PAGE_DATA.sessionId),fe=h(window.__PAGE_DATA.metadata),b=h([]),E=h(!0),Z=h(null),Ae=h("timeline"),I=h("timestamp"),M=h("asc"),L=h(null),P=h(null),X=h(!1),K=h(null),J=h(null),Ee=h(!1),$=h("\u{1F4CA} Copy as Mermaid Gantt"),ve=e=>e?typeof e=="string"?e:Array.isArray(e)?e.map(t=>t.text||t.content||"").join(" "):typeof e=="object"&&e.text?e.text:String(e):"",U=h(null),he=h(""),Ie=e=>{let t=e.currentTarget,a=t.querySelector(".gantt-bar-area");if(!a)return;let s=a.getBoundingClientRect(),n=t.getBoundingClientRect(),o=s.left-n.left,i=o+s.width,d=e.clientX-n.left;if(d>=o&&d<=i){U.value=d;let l=(d-o)/s.width,r=A.value+l*_.value,c=new Date(r),m=String(c.getHours()).padStart(2,"0"),p=String(c.getMinutes()).padStart(2,"0"),u=String(c.getSeconds()).padStart(2,"0");he.value=m+":"+p+":"+u}else U.value=null},De=()=>{U.value=null},_e=async()=>{let e=ie.value;if(!e.length)return;let t=l=>l?new Date(l).getTime():0,a=l=>(l||"").replace(/[`\n\r]/g,"").replace(/[:;#]/g,"-").replace(/\s+/g," ").trim().substring(0,100),s=l=>l?typeof l=="string"?l:Array.isArray(l)?l.map(r=>r.text||r.content||"").join(" "):typeof l=="object"&&l.text?l.text:String(l):"",n={},o=l=>{let r=l.replace(/[^a-zA-Z0-9]/g,"_").substring(0,30);return n[r]?(n[r]++,r+"_"+n[r]):(n[r]=1,r)},i=[];i.push("```mermaid"),i.push("gantt"),i.push(" title Session Timeline \u2013 "+a(C.value)),i.push(" dateFormat x"),i.push(" axisFormat %H:%M:%S"),i.push("");for(let l of e)if(l.rowType==="user-req"){let r=a(s(l.message)||"No message").substring(0,40),c="UserReq "+l.userReqNumber+" \u2013 "+r+" ("+W(l.duration)+")",m=o("userreq_"+l.userReqNumber),p=t(l.startTime),u=t(l.endTime);i.push(" "+c+" :milestone, "+m+", "+p+", "+u)}else if(l.rowType==="subagent"){let r=t(l.startTime),c=t(l.endTime),m=(l.toolCalls??0)+" tools",p=a(l.name)+" \u2013 "+W(l.duration)+" ("+m+")",u=o(l.name),g=l.status==="failed"?"crit, ":l.status==="incomplete"?"active, ":"";i.push(" "+p+" :"+g+u+", "+r+", "+c)}else if(l.rowType==="main-agent"){let r=t(l.startTime),c=t(l.endTime),m=a(l.summary||"idle"),p="Main Agent \u2013 "+W(l.duration)+" ("+m+")",u=o("main_agent");i.push(" "+p+" :"+u+", "+r+", "+c)}i.push("```"),i.push("");let d=i.join(`
2
- `);try{await navigator.clipboard.writeText(d),$.value="\u2705 Copied!"}catch{let r=document.createElement("textarea");r.value=d,r.style.position="fixed",r.style.opacity="0",document.body.appendChild(r),r.select(),document.execCommand("copy"),document.body.removeChild(r),$.value="\u2705 Copied!"}setTimeout(()=>{$.value="\u{1F4CA} Copy as Mermaid Gantt"},2e3)},D={"tool.execution_start":{color:"#d29922",shape:"diamond",label:"Tool Start"},"tool.execution_complete":{color:"#e3b341",shape:"diamond",label:"Tool Complete"},"assistant.message":{color:"#8b949e",shape:"circle",label:"Message"},"user.message":{color:"#79c0ff",shape:"square",label:"User Message"},"session.start":{color:"#56d364",shape:"square",label:"Session Start"},"session.resume":{color:"#56d364",shape:"square",label:"Session Resume"},"session.error":{color:"#f85149",shape:"triangle",label:"Error"},"session.truncation":{color:"#f0883e",shape:"triangle",label:"Truncation"},"session.compaction_start":{color:"#a371f7",shape:"square",label:"Compaction Start"},"session.compaction_complete":{color:"#bc8cff",shape:"square",label:"Compaction End"},"session.model_change":{color:"#f778ba",shape:"square",label:"Model Change"},abort:{color:"#ff7b72",shape:"triangle",label:"Abort"}},V=new Set(Object.keys(D)),W=e=>{if(e==null||e<0)return"\u2014";if(e<1e3)return Math.round(e)+"ms";let t=e/1e3;if(t<60){let o=Math.round(t*10)/10;return(o%1===0?Math.round(o):o.toFixed(1))+"s"}let a=Math.floor(t/60),s=Math.floor(t%60);return a<60?a+"m "+s+"s":Math.floor(a/60)+"h "+a%60+"m"},qe=e=>{if(!e)return"";let t=new Date(e);return String(t.getHours()).padStart(2,"0")+":"+String(t.getMinutes()).padStart(2,"0")+":"+String(t.getSeconds()).padStart(2,"0")},Ne=e=>e?new Date(e).toLocaleString():"",A=v(()=>{if(!b.value.length)return null;for(let e of b.value){let t=e.timestamp||e.snapshot?.timestamp;if(t)return new Date(t).getTime()}return null}),G=v(()=>{if(!b.value.length)return null;for(let e=b.value.length-1;e>=0;e--){let t=b.value[e],a=t.timestamp||t.snapshot?.timestamp;if(a)return new Date(a).getTime()}return null}),_=v(()=>!A.value||!G.value?0:G.value-A.value),S=v(()=>[...b.value].sort((e,t)=>{let a=e.timestamp?new Date(e.timestamp).getTime():0,s=t.timestamp?new Date(t.timestamp).getTime():0;return a!==s?a-s:(e._fileIndex??0)-(t._fileIndex??0)})),z=v(()=>{let e=S.value,t=new Set;for(let o of e)if(o.type==="subagent.started"){let i=o.data?.toolCallId;i&&t.add(i)}let a=new Map;for(let o of e)o.id&&a.set(o.id,o);let s=new Map,n=new Map;for(let o of e){if(o.type!=="tool.execution_start")continue;let i=o.parentId,d=0;for(;i&&d<10;){let l=a.get(i);if(!l)break;if(l.type==="assistant.message"){let r=l.data?.parentToolCallId;if(r&&t.has(r)){s.set(o.id,r);let c=o.data?.toolCallId;c&&n.set(c,r)}break}i=l.parentId,d++}}for(let o of e){if(o.type!=="tool.execution_complete")continue;let i=o.data?.toolCallId;i&&n.has(i)&&s.set(o.id,n.get(i))}return s}),B=v(()=>{let e=S.value,t=[],a=[];for(let n of e)if(n.type==="subagent.started")a.push(n);else if(n.type==="subagent.completed"||n.type==="subagent.failed"){let o=n.data?.toolCallId,i=-1;if(o){for(let f=a.length-1;f>=0;f--)if(a[f].data?.toolCallId===o){i=f;break}}i<0&&a.length>0&&(i=a.length-1);let d=i>=0?a.splice(i,1)[0]:null,l=d?.data?.agentDisplayName||d?.data?.agentName||"SubAgent",r=d?new Date(d.timestamp).getTime():null,c=new Date(n.timestamp).getTime(),m=r?c-r:null,p=d?.data?.toolCallId,u=0,g=[];if(d)for(let f of e){f.type==="tool.execution_start"&&p&&z.value.get(f.id)===p&&u++;let w=new Date(f.timestamp).getTime();w>=r&&w<=c&&V.has(f.type)&&(f.type!=="tool.execution_start"&&f.type!=="tool.execution_complete"?g.push({type:f.type,timestamp:w,data:f.data}):p&&z.value.get(f.id)===p&&g.push({type:f.type,timestamp:w,data:f.data}))}let T=Q(g,r,m);t.push({name:l,status:n.type==="subagent.completed"?"completed":"failed",startTime:d?.timestamp||null,endTime:n.timestamp,duration:m,toolCalls:u,innerEventMarkers:T})}let s=e.length>0?new Date(e[e.length-1].timestamp).getTime():Date.now();for(let n of a){let o=n.data?.agentDisplayName||n.data?.agentName||"SubAgent",i=new Date(n.timestamp).getTime(),d=s-i,l=n.data?.toolCallId,r=0,c=[];for(let p of e){p.type==="tool.execution_start"&&l&&z.value.get(p.id)===l&&r++;let u=new Date(p.timestamp).getTime();u>=i&&u<=s&&V.has(p.type)&&(p.type!=="tool.execution_start"&&p.type!=="tool.execution_complete"?c.push({type:p.type,timestamp:u,data:p.data}):l&&z.value.get(p.id)===l&&c.push({type:p.type,timestamp:u,data:p.data}))}let m=Q(c,i,d);t.push({name:o,status:"incomplete",startTime:n.timestamp,endTime:e[e.length-1]?.timestamp||n.timestamp,duration:d,toolCalls:r,innerEventMarkers:m})}return t.sort((n,o)=>{let i=n.startTime?new Date(n.startTime).getTime():0,d=o.startTime?new Date(o.startTime).getTime():0;return i-d})}),Re=v(()=>Math.max(...B.value.map(e=>e.duration||0),1)),Le=v(()=>{let e=B.value,t=e.filter(r=>r.status==="completed").length,a=e.filter(r=>r.status==="failed").length,s=e.filter(r=>r.status==="incomplete").length,n=e.length?(t/e.length*100).toFixed(0):100,o=e.filter(r=>r.startTime&&r.endTime).map(r=>[new Date(r.startTime).getTime(),new Date(r.endTime).getTime()]).sort((r,c)=>r[0]-c[0]),i=[];for(let[r,c]of o)!i.length||r>=i[i.length-1].e?i.push({s:r,e:c}):c>i[i.length-1].e&&(i[i.length-1].e=c);let d=i.reduce((r,c)=>r+(c.e-c.s),0),l=e.reduce((r,c)=>r+(c.toolCalls||0),0);return{completed:t,failed:a,incomplete:s,totalTime:d,totalTools:l,successRate:n}}),Q=(e,t,a)=>{if(!e.length||!a)return[];let s=new Set(["session.start","session.resume","session.error","session.truncation","session.compaction_start","session.compaction_complete","session.model_change","abort","user.message"]),n=[],o=[];for(let u of e)s.has(u.type)?n.push(u):o.push(u);let i=n.map(u=>{let g=(u.timestamp-t)/a*100,T=D[u.type]||{};return{type:u.type,position:Math.max(0,Math.min(100,g)),color:T.color||"#8b949e",shape:T.shape||"circle",label:T.label||u.type,timestamp:u.timestamp,toolName:u.data?.toolName||null}}),d=300*1e3,l=Math.max(d,a/20),r=new Map;for(let u of o){let g=Math.floor((u.timestamp-t)/l);r.has(g)||r.set(g,[]),r.get(g).push(u)}let c=u=>{let g=Math.round(210+38*u),T=Math.round(153+-72*u),f=Math.round(34+39*u);return"rgb("+g+","+T+","+f+")"},m=u=>u.type==="tool.execution_complete"&&(u.data?.isError||!!u.data?.error),p=[];for(let[u,g]of r){let f=(t+(u+.5)*l-t)/a*100,w=Math.max(0,Math.min(100,f)),q=g.filter(m).length,pe=g.filter(y=>y.type==="tool.execution_complete").length,k=pe>0?q/pe:0;if(g.length===1){let y=g[0],N=D[y.type]||{},ge=m(y)?"#f85149":N.color||"#8b949e";p.push({type:y.type,position:w,color:ge,shape:N.shape||"circle",label:m(y)?(N.label||y.type)+" (error)":N.label||y.type,timestamp:y.timestamp,toolName:y.data?.toolName||null})}else{let y={};g.forEach(R=>{let j=(D[R.type]||{}).label||R.type;y[j]=(y[j]||0)+1}),q>0&&(y.Errors=q);let N=Object.entries(y).map(([R,O])=>O+" "+R),ge=k>0?c(k):(()=>{let R=g.reduce((O,j)=>{let Se=g.filter(tt=>tt.type===j.type).length;return Se>O.cnt?{type:j.type,cnt:Se}:O},{type:g[0].type,cnt:0}).type;return(D[R]||{}).color||"#8b949e"})();p.push({type:"cluster",position:w,color:ge,shape:"cluster",label:N.join(", "),count:g.length,items:g})}}return[...i,...p].sort((u,g)=>u.position-g.position)},F=(e,t,a)=>{let s=a-t,n=[],o={},i=0;for(let c of e){let m=new Date(c.timestamp).getTime();if(m>=t&&m<=a)if(c.type.startsWith("tool."))c.id&&z.value.has(c.id)||(V.has(c.type)&&n.push({type:c.type,timestamp:m,data:c.data}),c.type==="tool.execution_start"&&i++,o.tool=(o.tool||0)+1);else{V.has(c.type)&&n.push({type:c.type,timestamp:m,data:c.data});let p="other";c.type.startsWith("assistant.")?p="message":c.type.startsWith("user.")?p="user":c.type.startsWith("session.")&&(p="session"),o[p]=(o[p]||0)+1}}let d=[];i&&d.push(i+" tool"+(i>1?"s":"")),o.message&&d.push(o.message+" message"+(o.message>1?"s":"")),o.user&&d.push(o.user+" user msg"),o.session&&d.push(o.session+" session event"+(o.session>1?"s":"")),o.other&&d.push(o.other+" other");let l=d.length?d.join(", "):"idle",r=Q(n,t,s);return{itemType:"agent-op",name:"Main Agent",summary:l,toolCalls:i,startTime:new Date(t).toISOString(),endTime:new Date(a).toISOString(),duration:s,eventCounts:o,innerEventMarkers:r}},ee=v(()=>{let e=B.value;if(!e.length)return[];let t=S.value,a=[];for(let s=0;s<e.length;s++){let n=e[s];if(s===0&&n.startTime){let l=A.value,r=new Date(n.startTime).getTime();r-l>500&&a.push(F(t,l,r))}a.push({...n,itemType:"subagent"});let o=e[s+1],i=new Date(n.endTime).getTime(),d=o?new Date(o.startTime).getTime():G.value;d-i>500&&a.push(F(t,i,d))}return a}),te=v(()=>{try{let e=S.value,t=e.filter(s=>s.type==="assistant.message"),a=e.filter(s=>s.type==="user.message");return t.map((s,n)=>{let o=s.timestamp;if(!o)return console.warn("[turnAnalysis] Message without timestamp:",s),null;let i=new Date(o).getTime();if(isNaN(i))return console.warn("[turnAnalysis] Invalid timestamp:",o,s),null;let d=t[n+1],r=(d?new Date(d.timestamp).getTime():G.value||i)-i,c=e.indexOf(s),m=e.slice(0,c).reverse().find(f=>f.type==="user.message"),p=m?a.indexOf(m)+1:0,u="",g=s.data?.message&&s.data.message.trim()!=="";g?u=ve(s.data.message):s.data?.tools&&s.data.tools.length>0?u=`Tool calls: ${s.data.tools.map(w=>w.name||"unknown").join(", ")}`:u="(empty assistant message)";let T=s.data?.tools?.length||0;return{turnId:s.id??`msg-${n}`,userReqNumber:p,message:ve(m?.data?.message||m?.data?.content||m?.data?.transformedContent||""),displayText:u,hasText:g,startTime:s.timestamp,endTime:d?.timestamp||b.value[b.value.length-1]?.timestamp,duration:r,toolCalls:T}}).filter(s=>s!==null)}catch(e){return console.error("[turnAnalysis] Error:",e),Z.value="Error analyzing turns: "+e.message,[]}}),Pe=v(()=>Math.max(...te.value.map(e=>e.duration||0),1)),ye=v(()=>{let e=new Map;for(let t of te.value){let a=t.userReqNumber||0;e.has(a)||e.set(a,{userReqNumber:a,message:t.message,turns:[]}),e.get(a).turns.push(t)}return Array.from(e.values()).sort((t,a)=>t.userReqNumber-a.userReqNumber)}),x=v(()=>{let e=S.value,t=new Map;for(let s of e)if(s.type==="tool.execution_start"){let n=s.data?.toolCallId;n&&t.set(n,{start:s})}else if(s.type==="tool.execution_complete"){let n=s.data?.toolCallId;n&&t.has(n)&&(t.get(n).complete=s)}let a=[];return t.forEach((s,n)=>{let o=new Date(s.start.timestamp).getTime(),i=s.complete?new Date(s.complete.timestamp).getTime():null,d=i?i-o:null,l=s.start.data?.toolName||s.start.data?.tool||"unknown",r=s.start.data?.arguments||{},c=s.complete?.data?.isError||!!s.complete?.data?.error,m="";l==="Bash"||l==="bash"||l==="exec"?m=r.command||r.description||"":["Read","read","Write","write","Edit","edit"].includes(l)?m=r.file_path||r.path||"":["Glob","glob"].includes(l)||["Grep","grep"].includes(l)?m=r.pattern||"":["Task","task"].includes(l)?m=r.description||r.prompt?.substring(0,80)||"":m=r.description||r.command||r.file_path||r.path||r.query||r.url||"",m.length>120&&(m=m.substring(0,120)+"..."),a.push({toolId:n,toolName:l,description:m,startTime:s.start.timestamp,endTime:s.complete?.timestamp||null,duration:d,isError:c,isRunning:!s.complete})}),a}),Ge=v(()=>{let e=[...x.value];return e.sort((t,a)=>{if(I.value==="duration")return M.value==="asc"?(t.duration||0)-(a.duration||0):(a.duration||0)-(t.duration||0);if(I.value==="toolName"){let o=(t.toolName||"").localeCompare(a.toolName||"");return M.value==="asc"?o:-o}let s=new Date(t.startTime).getTime(),n=new Date(a.startTime).getTime();return M.value==="asc"?s-n:n-s}),e}),ze=v(()=>Math.max(...x.value.map(e=>e.duration||0),1)),be=v(()=>{let e=["view","read","write","edit","create","glob","grep","notebookedit"],t={copilot_readfile:"read",copilot_createfile:"write",copilot_createdirectory:"write",copilot_findfiles:"search",copilot_findtextinfiles:"search",copilot_listdirectory:"read",textedit:"edit",copilot_replacestring:"edit",copilot_multireplacestring:"edit"},a=[];for(let s of b.value)if(s.type==="tool.execution_start"){let n=s.data?.toolName?.toLowerCase()||"",o=s.data?.arguments||{},i=o.path||o.file||o.directory||o.pattern||"";if(e.includes(n)){if(i){let d="other";n==="view"||n==="read"?d="read":n==="write"||n==="notebookedit"||n==="create"?d="write":n==="edit"?d="edit":(n==="glob"||n==="grep")&&(d="search"),a.push({toolName:s.data?.toolName||n,opType:d,filePath:i,timestamp:s.timestamp,startTime:s.timestamp})}}else if(t[n]){let d=t[n];a.push({toolName:s.data?.toolName||n,opType:d,filePath:i||"(implicit)",timestamp:s.timestamp,startTime:s.timestamp})}}return a.sort((s,n)=>new Date(s.startTime)-new Date(n.startTime))}),Be=v(()=>{let e=be.value;return{uniqueCount:new Set(e.map(a=>a.filePath)).size,totalOps:e.length,reads:e.filter(a=>a.opType==="read").length,writes:e.filter(a=>a.opType==="write").length,edits:e.filter(a=>a.opType==="edit").length,searches:e.filter(a=>a.opType==="search").length}}),Te=v(()=>{let e={};return x.value.forEach(t=>{let a=(t.toolName||"unknown").toLowerCase(),s;["bash","exec"].includes(a)?s="Bash/Exec":["read"].includes(a)?s="Read":["write"].includes(a)?s="Write":["edit"].includes(a)?s="Edit":["glob"].includes(a)?s="Glob":["grep"].includes(a)?s="Grep":["task"].includes(a)?s="Task (SubAgent)":["web_search","websearch"].includes(a)?s="Web Search":["web_fetch","webfetch"].includes(a)?s="Web Fetch":s=t.toolName||"Other",e[s]||(e[s]={category:s,totalTime:0,count:0,errors:0}),e[s].totalTime+=t.duration||0,e[s].count++,t.isError&&e[s].errors++}),Object.values(e).sort((t,a)=>a.totalTime-t.totalTime)}),Fe=v(()=>Math.max(...Te.value.map(e=>e.totalTime),1)),se=v(()=>{let e=x.value.filter(n=>n.duration&&n.startTime&&n.endTime).map(n=>({start:new Date(n.startTime).getTime(),end:new Date(n.endTime).getTime()})).sort((n,o)=>n.start-o.start);if(!e.length)return 0;let t=0,a=e[0].start,s=e[0].end;for(let n=1;n<e.length;n++)e[n].start<=s?s=Math.max(s,e[n].end):(t+=s-a,a=e[n].start,s=e[n].end);return t+=s-a,t}),nt=v(()=>{let e=0,t={};for(let a of b.value)if(a.type==="tool.execution_complete"&&a.data?.toolTelemetry?.metrics){let s=a.data.toolTelemetry.metrics.resultForLlmLength||0;e+=s;let n=a.data.toolName||"unknown";t[n]||(t[n]=0),t[n]+=s}return{total:e,byCategory:t}}),ae=v(()=>{let e=S.value,t=[];for(let a=0;a<e.length-1;a++){let s=e[a],n=e[a+1],o=new Date(s.timestamp).getTime(),d=new Date(n.timestamp).getTime()-o;if(d<100)continue;let l=null,r="";s.type==="user.message"&&n.type==="assistant.turn_start"?(l="input-consumption",r=`LLM reading user input (${(s.data?.message||"").length} chars)`):s.type==="assistant.turn_start"&&n.type==="assistant.message"?(l="llm-generation",r=`LLM generating response (${(n.data?.content||"").length} chars output)`):s.type==="assistant.turn_start"&&n.type==="tool.execution_start"?(l="llm-generation",r=`LLM deciding to call ${n.data?.toolName||"unknown"}`):s.type==="assistant.message"&&n.type==="assistant.turn_start"?(l="turn-gap",r="Gap between assistant response and next turn"):s.type==="tool.execution_complete"&&d>500?(l="post-tool",r=`Processing ${s.data?.toolName||"unknown"} result`):d>5e3&&(l="idle",r=`${s.type} \u2192 ${n.type}`),l&&t.push({type:l,description:r,startTime:s.timestamp,endTime:n.timestamp,duration:d,fromEvent:s.type,toEvent:n.type,fromData:s.data,toData:n.data})}return t.sort((a,s)=>(s.duration||0)-(a.duration||0))}),Oe=v(()=>Math.max(...ae.value.map(e=>e.duration||0),1)),je=v(()=>{let e={"input-consumption":{count:0,total:0,avg:0},"llm-generation":{count:0,total:0,avg:0},"post-tool":{count:0,total:0,avg:0},"turn-gap":{count:0,total:0,avg:0},idle:{count:0,total:0,avg:0}};return ae.value.forEach(t=>{e[t.type]&&(e[t.type].count++,e[t.type].total+=t.duration)}),Object.keys(e).forEach(t=>{e[t].count>0&&(e[t].avg=e[t].total/e[t].count)}),e}),$e=v(()=>{let e=x.value.length;if(e===0)return 100;let t=x.value.filter(a=>a.isError).length;return((e-t)/e*100).toFixed(1)}),Ue=v(()=>x.value.filter(e=>e.isError).length),Ve=v(()=>{let e=S.value,t=0;for(let o=0;o<e.length-1;o++){let i=e[o],d=e[o+1];if(d.type==="user.message"&&i.type!=="user.message"){let l=new Date(d.timestamp).getTime()-new Date(i.timestamp).getTime();l>1e3&&(t+=l)}}let a=_.value||0,s=Math.max(a-t,0),n=Math.max(s-se.value,0);return{userThinkingTime:t,agentWorkingTime:s,llmTime:n,userThinkingPct:a>0?(t/a*100).toFixed(0):0,agentWorkingPct:a>0?(s/a*100).toFixed(0):0,llmPct:a>0?(n/a*100).toFixed(0):0,toolPct:a>0?(se.value/a*100).toFixed(0):0}}),ne=v(()=>x.value.length),We=v(()=>ne.value?x.value.reduce((t,a)=>t+(a.duration||0),0)/ne.value:0),He=v(()=>x.value.length?x.value.reduce((e,t)=>(t.duration||0)>(e.duration||0)?t:e):null),oe=v(()=>{let e=S.value;if(e.some(n=>n.data?.source==="vscode"))return!0;let a=e.some(n=>n.type==="assistant.message"&&n.data?.subAgentName),s=e.some(n=>n.type==="subagent.started"||n.type==="subagent.completed"||n.type==="subagent.failed");return a&&!s}),we=v(()=>{if(!oe.value)return[];let e=S.value,t=new Map;for(let a=0;a<e.length;a++){let s=e[a];if(s.type==="assistant.message"&&s.data?.subAgentName){let n=s.data.subAgentId||s.data.subAgentName;t.has(n)||t.set(n,{name:s.data.subAgentName,events:[],toolCount:0,firstIndex:a,status:"completed",subAgentId:s.data.subAgentId});let o=t.get(n);o.events.push(s),s.data.tools&&Array.isArray(s.data.tools)&&(o.toolCount+=s.data.tools.length),(s.data.error||s.data.status==="error")&&(o.status="failed")}}return Array.from(t.values()).sort((a,s)=>a.firstIndex-s.firstIndex)}),ie=v(()=>{let e=[],t=ye.value,a=B.value,s=S.value;if(oe.value){let n=we.value,o=[];for(let i=0;i<s.length;i++)s[i].type==="user.message"&&o.push({event:s[i],sortedIndex:i});if(o.length>0&&n.length>0)for(let i=0;i<o.length;i++){let{event:d,sortedIndex:l}=o[i],r=o[i+1]?o[i+1].sortedIndex:1/0,c=n.filter(u=>u.firstIndex>=l&&u.firstIndex<r),m=c.reduce((u,g)=>u+g.toolCount,0),p=d.data?.message||d.data?.content||"";e.push({rowType:"user-req",userReqNumber:i+1,message:typeof p=="string"?p.substring(0,120):String(p).substring(0,120),toolCount:m,sequenceIndex:l,isSequenceEstimated:!0,duration:m});for(let u of c)e.push({rowType:"subagent",itemType:"subagent",name:u.name,status:u.status,toolCount:u.toolCount,sequenceIndex:u.firstIndex,isSequenceEstimated:!0,duration:u.toolCount,indented:!0})}else if(n.length>0)for(let i of n)e.push({rowType:"subagent",itemType:"subagent",name:i.name,status:i.status,toolCount:i.toolCount,sequenceIndex:i.firstIndex,isSequenceEstimated:!0,duration:i.toolCount});return e}if(t.length)for(let n=0;n<t.length;n++){let o=t[n],i=o.turns;if(!i.length)continue;let d=new Date(i[0].startTime).getTime(),l=new Date(i[i.length-1].endTime).getTime();e.push({rowType:"user-req",userReqNumber:o.userReqNumber,message:o.message,startTime:i[0].startTime,endTime:i[i.length-1].endTime,duration:l-d});let r=a.filter(c=>{if(!c.startTime)return!1;let m=new Date(c.startTime).getTime();return m>=d&&m<=l});if(r.length)for(let c=0;c<r.length;c++){let m=r[c],p=c===0?d:new Date(r[c-1].endTime).getTime(),u=new Date(m.startTime).getTime();if(u-p>500){let g=F(s,p,u);g.rowType="main-agent",e.push(g)}if(e.push({...m,rowType:"subagent",itemType:"subagent"}),c===r.length-1){let g=new Date(m.endTime).getTime(),T=l;if(T-g>500){let f=F(s,g,T);f.rowType="main-agent",e.push(f)}}}else if(l-d>0){let c=F(s,d,l);c.rowType="main-agent",e.push(c)}}else if(ee.value.length)for(let n of ee.value)e.push({...n,rowType:n.itemType==="agent-op"?"main-agent":"subagent"});return e}),Ye=(e,t)=>{if(!A.value||!_.value||!e)return{left:"0%",width:"0%"};let a=new Date(e).getTime(),s=t?new Date(t).getTime():a+1e3,n=(a-A.value)/_.value*100,o=Math.max((s-a)/_.value*100,.5);return{left:n+"%",width:Math.min(o,100-n)+"%"}},re=e=>{let t=ie.value;if(t.length===0)return{left:"0%",width:"0%"};if(e.rowType==="user-req"){let c=t.findIndex(f=>f===e);if(c===-1)return{left:"0%",width:"0%"};let m=[];for(let f=c+1;f<t.length&&t[f].rowType!=="user-req";f++)t[f].rowType==="subagent"&&m.push(t[f]);if(m.length===0){let w=t.filter(k=>k.rowType!=="user-req").reduce((k,y)=>k+(y.toolCount||0),0);if(w===0)return{left:"0%",width:"0%"};let q=0;for(let k=0;k<c;k++)t[k].rowType==="subagent"&&(q+=t[k].toolCount||0);return{left:q/w*100+"%",width:Math.max(1,1/w*100)+"%"}}let p=re(m[0]),u=re(m[m.length-1]),g=parseFloat(p.left),T=parseFloat(u.left)+parseFloat(u.width);return{left:g+"%",width:T-g+"%"}}if(t.findIndex(c=>c===e)===-1)return{left:"0%",width:"0%"};let s=t.filter(c=>c.rowType!=="user-req"),n=s.reduce((c,m)=>c+(m.toolCount||0),0);if(n===0)return{left:"0%",width:"0%"};let o=s.findIndex(c=>c===e);if(o===-1)return{left:"0%",width:"0%"};let i=0;for(let c=0;c<o;c++)i+=s[c].toolCount||0;let d=i/n*100,l=e.toolCount||0,r=Math.max(l/n*100,2);return{left:d+"%",width:Math.min(r,100-d)+"%"}},Ze=e=>{I.value===e?M.value=M.value==="asc"?"desc":"asc":(I.value=e,M.value=e==="duration"?"desc":"asc")},Xe=e=>I.value!==e?"\u2195":M.value==="asc"?"\u2191":"\u2193",Ke=e=>{let t=(e||"").toLowerCase();return["bash","exec"].includes(t)?"badge-bash":t==="read"?"badge-read":t==="write"||t==="notebookedit"?"badge-write":t==="edit"?"badge-edit":t==="glob"||t==="grep"?"badge-search":t==="task"?"badge-subagent":"badge-other"},Je=e=>({read:"badge-read",write:"badge-write",edit:"badge-edit",create:"badge-create",search:"badge-search"})[e]||"badge-other";Ce(async()=>{try{let e=await fetch("/api/sessions/"+C.value+"/events");if(!e.ok)throw new Error("Failed to load events: "+e.statusText);let t=await e.json();console.log("[TIME-ANALYZE] Loaded events:",t.length),console.log("[TIME-ANALYZE] Event types:",[...new Set(t.map(a=>a.type))]),console.log("[TIME-ANALYZE] Turn starts:",t.filter(a=>a.type==="assistant.turn_start").length),console.log("[TIME-ANALYZE] User messages:",t.filter(a=>a.type==="user.message").length),b.value=t.sort((a,s)=>{let n=a.timestamp?new Date(a.timestamp).getTime():0,o=s.timestamp?new Date(s.timestamp).getTime():0;return n!==o?n-o:(a._fileIndex??0)-(s._fileIndex??0)}),console.log("[TIME-ANALYZE] Events set, length:",b.value.length)}catch(e){console.error("[TIME-ANALYZE] Error loading events:",e),Z.value=e.message}finally{E.value=!1}});let le=h("not_started"),ce=h(null),H=h(null),de=h(0),Y=null,Qe=v(()=>L.value?marked.parse(L.value):""),xe=async()=>{try{let t=await(await fetch(`/session/${C.value}/insight`)).json();le.value=t.status,t.status==="completed"?(L.value=t.report,P.value=null,J.value=t.generatedAt,me()):t.status==="generating"?(P.value=t.log||null,H.value=t.startedAt,ce.value=t.lastUpdate,de.value=t.ageMs,ue(),Vue.nextTick(()=>{let a=document.getElementById("insight-log");a&&(a.scrollTop=a.scrollHeight)})):t.status==="timeout"&&(P.value=t.log||null,H.value=t.startedAt,ce.value=t.lastUpdate,de.value=t.ageMs,ue())}catch(e){console.error("Failed to check insight:",e)}},ue=()=>{me(),Y=setInterval(xe,2e3)},me=()=>{Y&&(clearInterval(Y),Y=null)},ke=async(e=!1)=>{X.value=!0,K.value=null,P.value=null;try{let t=await fetch(`/session/${C.value}/insight`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({force:e})});if(!t.ok){let s=await t.json();throw new Error(s.error||"Failed to generate insight")}let a=await t.json();le.value=a.status,a.status==="generating"?(H.value=a.startedAt,ue()):a.status==="completed"&&(L.value=a.report,J.value=a.generatedAt)}catch(t){K.value=t.message}finally{X.value=!1}},et=async()=>{await ke(!0)};return Ce(async()=>{await xe()}),at(()=>{me()}),{sessionId:C,metadata:fe,events:b,loading:E,error:Z,activeTab:Ae,sortField:I,sortDir:M,insightReport:L,insightLog:P,insightLoading:X,insightError:K,insightGeneratedAt:J,insightStatus:le,insightLastUpdate:ce,insightStartedAt:H,insightAgeMs:de,renderedInsight:Qe,generateInsight:ke,regenerateInsight:et,formatDuration:W,formatTime:qe,formatDateTime:Ne,sessionStart:A,sessionEnd:G,totalDuration:_,subagentAnalysis:B,maxSubagentDuration:Re,subagentTimelineItems:ee,subagentStats:Le,EVENT_MARKER_CATEGORIES:D,showMarkerLegend:Ee,copyLabel:$,copyTimelineMarkdown:_e,ganttCrosshairX:U,ganttCrosshairTime:he,onGanttMouseMove:Ie,onGanttMouseLeave:De,turnAnalysis:te,maxTurnDuration:Pe,groupedTurns:ye,unifiedTimelineItems:ie,toolAnalysis:x,sortedToolAnalysis:Ge,maxToolDuration:ze,fileOperations:be,fileStats:Be,toolTimeByCategory:Te,maxCategoryTime:Fe,totalToolTime:se,totalToolCount:ne,avgToolDuration:We,longestTool:He,successRate:$e,errorCount:Ue,timeBreakdown:Ve,gapAnalysis:ae,maxGapDuration:Oe,gapStats:je,ganttPosition:Ye,ganttSequencePosition:re,toggleSort:Ze,sortIcon:Xe,getToolBadgeClass:Ke,getOpBadgeClass:Je,isVSCodeSession:oe,vsCodeSubagents:we}},template:`
2
+ `);try{await navigator.clipboard.writeText(d),$.value="\u2705 Copied!"}catch{let r=document.createElement("textarea");r.value=d,r.style.position="fixed",r.style.opacity="0",document.body.appendChild(r),r.select(),document.execCommand("copy"),document.body.removeChild(r),$.value="\u2705 Copied!"}setTimeout(()=>{$.value="\u{1F4CA} Copy as Mermaid Gantt"},2e3)},D={"tool.execution_start":{color:"#d29922",shape:"diamond",label:"Tool Start"},"tool.execution_complete":{color:"#e3b341",shape:"diamond",label:"Tool Complete"},"assistant.message":{color:"#8b949e",shape:"circle",label:"Message"},"user.message":{color:"#79c0ff",shape:"square",label:"User Message"},"session.start":{color:"#56d364",shape:"square",label:"Session Start"},"session.resume":{color:"#56d364",shape:"square",label:"Session Resume"},"session.error":{color:"#f85149",shape:"triangle",label:"Error"},"session.truncation":{color:"#f0883e",shape:"triangle",label:"Truncation"},"session.compaction_start":{color:"#a371f7",shape:"square",label:"Compaction Start"},"session.compaction_complete":{color:"#bc8cff",shape:"square",label:"Compaction End"},"session.model_change":{color:"#f778ba",shape:"square",label:"Model Change"},abort:{color:"#ff7b72",shape:"triangle",label:"Abort"}},V=new Set(Object.keys(D)),W=e=>{if(e==null||e<0)return"\u2014";if(e<1e3)return Math.round(e)+"ms";let t=e/1e3;if(t<60){let o=Math.round(t*10)/10;return(o%1===0?Math.round(o):o.toFixed(1))+"s"}let a=Math.floor(t/60),s=Math.floor(t%60);return a<60?a+"m "+s+"s":Math.floor(a/60)+"h "+a%60+"m"},qe=e=>{if(!e)return"";let t=new Date(e);return String(t.getHours()).padStart(2,"0")+":"+String(t.getMinutes()).padStart(2,"0")+":"+String(t.getSeconds()).padStart(2,"0")},Ne=e=>e?new Date(e).toLocaleString():"",A=v(()=>{if(!b.value.length)return null;for(let e of b.value){let t=e.timestamp||e.snapshot?.timestamp;if(t)return new Date(t).getTime()}return null}),G=v(()=>{if(!b.value.length)return null;for(let e=b.value.length-1;e>=0;e--){let t=b.value[e],a=t.timestamp||t.snapshot?.timestamp;if(a)return new Date(a).getTime()}return null}),_=v(()=>!A.value||!G.value?0:G.value-A.value),S=v(()=>[...b.value].sort((e,t)=>{let a=e.timestamp?new Date(e.timestamp).getTime():0,s=t.timestamp?new Date(t.timestamp).getTime():0;return a!==s?a-s:(e._fileIndex??0)-(t._fileIndex??0)})),z=v(()=>{let e=S.value,t=new Set;for(let o of e)if(o.type==="subagent.started"){let i=o.data?.toolCallId;i&&t.add(i)}let a=new Map;for(let o of e)o.id&&a.set(o.id,o);let s=new Map,n=new Map;for(let o of e){if(o.type!=="tool.execution_start")continue;let i=o.parentId,d=0;for(;i&&d<10;){let l=a.get(i);if(!l)break;if(l.type==="assistant.message"){let r=l.data?.parentToolCallId;if(r&&t.has(r)){s.set(o.id,r);let c=o.data?.toolCallId;c&&n.set(c,r)}break}i=l.parentId,d++}}for(let o of e){if(o.type!=="tool.execution_complete")continue;let i=o.data?.toolCallId;i&&n.has(i)&&s.set(o.id,n.get(i))}return s}),B=v(()=>{let e=S.value,t=[],a=[];for(let n of e)if(n.type==="subagent.started")a.push(n);else if(n.type==="subagent.completed"||n.type==="subagent.failed"){let o=n.data?.toolCallId,i=-1;if(o){for(let f=a.length-1;f>=0;f--)if(a[f].data?.toolCallId===o){i=f;break}}i<0&&a.length>0&&(i=a.length-1);let d=i>=0?a.splice(i,1)[0]:null,l=d?.data?.agentDisplayName||d?.data?.agentName||"SubAgent",r=d?new Date(d.timestamp).getTime():null,c=new Date(n.timestamp).getTime(),m=r?c-r:null,p=d?.data?.toolCallId,u=0,g=[];if(d)for(let f of e){f.type==="tool.execution_start"&&p&&z.value.get(f.id)===p&&u++;let w=new Date(f.timestamp).getTime();w>=r&&w<=c&&V.has(f.type)&&(f.type!=="tool.execution_start"&&f.type!=="tool.execution_complete"?g.push({type:f.type,timestamp:w,data:f.data}):p&&z.value.get(f.id)===p&&g.push({type:f.type,timestamp:w,data:f.data}))}let T=Q(g,r,m);t.push({name:l,status:n.type==="subagent.completed"?"completed":"failed",startTime:d?.timestamp||null,endTime:n.timestamp,duration:m,toolCalls:u,innerEventMarkers:T})}let s=e.length>0?new Date(e[e.length-1].timestamp).getTime():Date.now();for(let n of a){let o=n.data?.agentDisplayName||n.data?.agentName||"SubAgent",i=new Date(n.timestamp).getTime(),d=s-i,l=n.data?.toolCallId,r=0,c=[];for(let p of e){p.type==="tool.execution_start"&&l&&z.value.get(p.id)===l&&r++;let u=new Date(p.timestamp).getTime();u>=i&&u<=s&&V.has(p.type)&&(p.type!=="tool.execution_start"&&p.type!=="tool.execution_complete"?c.push({type:p.type,timestamp:u,data:p.data}):l&&z.value.get(p.id)===l&&c.push({type:p.type,timestamp:u,data:p.data}))}let m=Q(c,i,d);t.push({name:o,status:"incomplete",startTime:n.timestamp,endTime:e[e.length-1]?.timestamp||n.timestamp,duration:d,toolCalls:r,innerEventMarkers:m})}return t.sort((n,o)=>{let i=n.startTime?new Date(n.startTime).getTime():0,d=o.startTime?new Date(o.startTime).getTime():0;return i-d})}),Re=v(()=>Math.max(...B.value.map(e=>e.duration||0),1)),Le=v(()=>{let e=B.value,t=e.filter(r=>r.status==="completed").length,a=e.filter(r=>r.status==="failed").length,s=e.filter(r=>r.status==="incomplete").length,n=e.length?(t/e.length*100).toFixed(0):100,o=e.filter(r=>r.startTime&&r.endTime).map(r=>[new Date(r.startTime).getTime(),new Date(r.endTime).getTime()]).sort((r,c)=>r[0]-c[0]),i=[];for(let[r,c]of o)!i.length||r>=i[i.length-1].e?i.push({s:r,e:c}):c>i[i.length-1].e&&(i[i.length-1].e=c);let d=i.reduce((r,c)=>r+(c.e-c.s),0),l=e.reduce((r,c)=>r+(c.toolCalls||0),0);return{completed:t,failed:a,incomplete:s,totalTime:d,totalTools:l,successRate:n}}),Q=(e,t,a)=>{if(!e.length||!a)return[];let s=new Set(["session.start","session.resume","session.error","session.truncation","session.compaction_start","session.compaction_complete","session.model_change","abort","user.message"]),n=[],o=[];for(let u of e)s.has(u.type)?n.push(u):o.push(u);let i=n.map(u=>{let g=(u.timestamp-t)/a*100,T=D[u.type]||{};return{type:u.type,position:Math.max(0,Math.min(100,g)),color:T.color||"#8b949e",shape:T.shape||"circle",label:T.label||u.type,timestamp:u.timestamp,toolName:u.data?.toolName||null}}),d=300*1e3,l=Math.max(d,a/20),r=new Map;for(let u of o){let g=Math.floor((u.timestamp-t)/l);r.has(g)||r.set(g,[]),r.get(g).push(u)}let c=u=>{let g=Math.round(210+38*u),T=Math.round(153+-72*u),f=Math.round(34+39*u);return"rgb("+g+","+T+","+f+")"},m=u=>u.type==="tool.execution_complete"&&(u.data?.isError||!!u.data?.error),p=[];for(let[u,g]of r){let f=(t+(u+.5)*l-t)/a*100,w=Math.max(0,Math.min(100,f)),q=g.filter(m).length,pe=g.filter(y=>y.type==="tool.execution_complete").length,k=pe>0?q/pe:0;if(g.length===1){let y=g[0],N=D[y.type]||{},ge=m(y)?"#f85149":N.color||"#8b949e";p.push({type:y.type,position:w,color:ge,shape:N.shape||"circle",label:m(y)?(N.label||y.type)+" (error)":N.label||y.type,timestamp:y.timestamp,toolName:y.data?.toolName||null})}else{let y={};g.forEach(R=>{let j=(D[R.type]||{}).label||R.type;y[j]=(y[j]||0)+1}),q>0&&(y.Errors=q);let N=Object.entries(y).map(([R,O])=>O+" "+R),ge=k>0?c(k):(()=>{let R=g.reduce((O,j)=>{let Se=g.filter(tt=>tt.type===j.type).length;return Se>O.cnt?{type:j.type,cnt:Se}:O},{type:g[0].type,cnt:0}).type;return(D[R]||{}).color||"#8b949e"})();p.push({type:"cluster",position:w,color:ge,shape:"cluster",label:N.join(", "),count:g.length,items:g})}}return[...i,...p].sort((u,g)=>u.position-g.position)},F=(e,t,a)=>{let s=a-t,n=[],o={},i=0;for(let c of e){let m=new Date(c.timestamp).getTime();if(m>=t&&m<=a)if(c.type.startsWith("tool."))c.id&&z.value.has(c.id)||(V.has(c.type)&&n.push({type:c.type,timestamp:m,data:c.data}),c.type==="tool.execution_start"&&i++,o.tool=(o.tool||0)+1);else{V.has(c.type)&&n.push({type:c.type,timestamp:m,data:c.data});let p="other";c.type.startsWith("assistant.")?p="message":c.type.startsWith("user.")?p="user":c.type.startsWith("session.")&&(p="session"),o[p]=(o[p]||0)+1}}let d=[];i&&d.push(i+" tool"+(i>1?"s":"")),o.message&&d.push(o.message+" message"+(o.message>1?"s":"")),o.user&&d.push(o.user+" user msg"),o.session&&d.push(o.session+" session event"+(o.session>1?"s":"")),o.other&&d.push(o.other+" other");let l=d.length?d.join(", "):"idle",r=Q(n,t,s);return{itemType:"agent-op",name:"Main Agent",summary:l,toolCalls:i,startTime:new Date(t).toISOString(),endTime:new Date(a).toISOString(),duration:s,eventCounts:o,innerEventMarkers:r}},ee=v(()=>{let e=B.value;if(!e.length)return[];let t=S.value,a=[];for(let s=0;s<e.length;s++){let n=e[s];if(s===0&&n.startTime){let l=A.value,r=new Date(n.startTime).getTime();r-l>500&&a.push(F(t,l,r))}a.push({...n,itemType:"subagent"});let o=e[s+1],i=new Date(n.endTime).getTime(),d=o?new Date(o.startTime).getTime():G.value;d-i>500&&a.push(F(t,i,d))}return a}),te=v(()=>{try{let e=S.value,t=e.filter(s=>s.type==="assistant.message"),a=e.filter(s=>s.type==="user.message");return t.map((s,n)=>{let o=s.timestamp;if(!o)return console.warn("[turnAnalysis] Message without timestamp:",s),null;let i=new Date(o).getTime();if(isNaN(i))return console.warn("[turnAnalysis] Invalid timestamp:",o,s),null;let d=t[n+1],r=(d?new Date(d.timestamp).getTime():G.value||i)-i,c=e.indexOf(s),m=e.slice(0,c).reverse().find(f=>f.type==="user.message"),p=m?a.indexOf(m)+1:0,u="",g=s.data?.message&&s.data.message.trim()!=="";g?u=ve(s.data.message):s.data?.tools&&s.data.tools.length>0?u=`Tool calls: ${s.data.tools.map(w=>w.name||"unknown").join(", ")}`:u="(empty assistant message)";let T=s.data?.tools?.length||0;return{turnId:s.id??`msg-${n}`,userReqNumber:p,message:ve(m?.data?.message||m?.data?.content||m?.data?.transformedContent||""),displayText:u,hasText:g,startTime:s.timestamp,endTime:d?.timestamp||b.value[b.value.length-1]?.timestamp,duration:r,toolCalls:T}}).filter(s=>s!==null)}catch(e){return console.error("[turnAnalysis] Error:",e),Z.value="Error analyzing turns: "+e.message,[]}}),Pe=v(()=>Math.max(...te.value.map(e=>e.duration||0),1)),ye=v(()=>{let e=new Map;for(let t of te.value){let a=t.userReqNumber||0;e.has(a)||e.set(a,{userReqNumber:a,message:t.message,turns:[]}),e.get(a).turns.push(t)}return Array.from(e.values()).sort((t,a)=>t.userReqNumber-a.userReqNumber)}),x=v(()=>{let e=S.value,t=new Map;for(let s of e)if(s.type==="tool.execution_start"){let n=s.data?.toolCallId;n&&t.set(n,{start:s})}else if(s.type==="tool.execution_complete"){let n=s.data?.toolCallId;n&&t.has(n)&&(t.get(n).complete=s)}let a=[];return t.forEach((s,n)=>{let o=new Date(s.start.timestamp).getTime(),i=s.complete?new Date(s.complete.timestamp).getTime():null,d=i?i-o:null,l=s.start.data?.toolName||s.start.data?.tool||"unknown",r=s.start.data?.arguments||{},c=s.complete?.data?.isError||!!s.complete?.data?.error,m="";l==="Bash"||l==="bash"||l==="exec"?m=r.command||r.description||"":["Read","read","Write","write","Edit","edit"].includes(l)?m=r.file_path||r.path||"":["Glob","glob"].includes(l)||["Grep","grep"].includes(l)?m=r.pattern||"":["Task","task"].includes(l)?m=r.description||r.prompt?.substring(0,80)||"":m=r.description||r.command||r.file_path||r.path||r.query||r.url||"",m.length>120&&(m=m.substring(0,120)+"..."),a.push({toolId:n,toolName:l,description:m,startTime:s.start.timestamp,endTime:s.complete?.timestamp||null,duration:d,isError:c,isRunning:!s.complete})}),a}),Ge=v(()=>{let e=[...x.value];return e.sort((t,a)=>{if(I.value==="duration")return M.value==="asc"?(t.duration||0)-(a.duration||0):(a.duration||0)-(t.duration||0);if(I.value==="toolName"){let o=(t.toolName||"").localeCompare(a.toolName||"");return M.value==="asc"?o:-o}let s=new Date(t.startTime).getTime(),n=new Date(a.startTime).getTime();return M.value==="asc"?s-n:n-s}),e}),ze=v(()=>Math.max(...x.value.map(e=>e.duration||0),1)),be=v(()=>{let e=["view","read","write","edit","create","glob","grep","notebookedit"],t={copilot_readfile:"read",copilot_createfile:"write",copilot_createdirectory:"write",copilot_findfiles:"search",copilot_findtextinfiles:"search",copilot_listdirectory:"read",textedit:"edit",copilot_replacestring:"edit",copilot_multireplacestring:"edit"},a=[];for(let s of b.value)if(s.type==="tool.execution_start"){let n=s.data?.toolName?.toLowerCase()||"",o=s.data?.arguments||{},i=o.path||o.file||o.directory||o.pattern||"";if(e.includes(n)){if(i){let d="other";n==="view"||n==="read"?d="read":n==="write"||n==="notebookedit"||n==="create"?d="write":n==="edit"?d="edit":(n==="glob"||n==="grep")&&(d="search"),a.push({toolName:s.data?.toolName||n,opType:d,filePath:i,timestamp:s.timestamp,startTime:s.timestamp})}}else if(t[n]){let d=t[n];a.push({toolName:s.data?.toolName||n,opType:d,filePath:i||"(implicit)",timestamp:s.timestamp,startTime:s.timestamp})}}return a.sort((s,n)=>new Date(s.startTime)-new Date(n.startTime))}),Be=v(()=>{let e=be.value;return{uniqueCount:new Set(e.map(a=>a.filePath)).size,totalOps:e.length,reads:e.filter(a=>a.opType==="read").length,writes:e.filter(a=>a.opType==="write").length,edits:e.filter(a=>a.opType==="edit").length,searches:e.filter(a=>a.opType==="search").length}}),Te=v(()=>{let e={};return x.value.forEach(t=>{let a=(t.toolName||"unknown").toLowerCase(),s;["bash","exec"].includes(a)?s="Bash/Exec":["read"].includes(a)?s="Read":["write"].includes(a)?s="Write":["edit"].includes(a)?s="Edit":["glob"].includes(a)?s="Glob":["grep"].includes(a)?s="Grep":["task"].includes(a)?s="Task (SubAgent)":["web_search","websearch"].includes(a)?s="Web Search":["web_fetch","webfetch"].includes(a)?s="Web Fetch":s=t.toolName||"Other",e[s]||(e[s]={category:s,totalTime:0,count:0,errors:0}),e[s].totalTime+=t.duration||0,e[s].count++,t.isError&&e[s].errors++}),Object.values(e).sort((t,a)=>a.count-t.count)}),Fe=v(()=>Math.max(...Te.value.map(e=>e.totalTime),1)),se=v(()=>{let e=x.value.filter(n=>n.duration&&n.startTime&&n.endTime).map(n=>({start:new Date(n.startTime).getTime(),end:new Date(n.endTime).getTime()})).sort((n,o)=>n.start-o.start);if(!e.length)return 0;let t=0,a=e[0].start,s=e[0].end;for(let n=1;n<e.length;n++)e[n].start<=s?s=Math.max(s,e[n].end):(t+=s-a,a=e[n].start,s=e[n].end);return t+=s-a,t}),nt=v(()=>{let e=0,t={};for(let a of b.value)if(a.type==="tool.execution_complete"&&a.data?.toolTelemetry?.metrics){let s=a.data.toolTelemetry.metrics.resultForLlmLength||0;e+=s;let n=a.data.toolName||"unknown";t[n]||(t[n]=0),t[n]+=s}return{total:e,byCategory:t}}),ae=v(()=>{let e=S.value,t=[];for(let a=0;a<e.length-1;a++){let s=e[a],n=e[a+1],o=new Date(s.timestamp).getTime(),d=new Date(n.timestamp).getTime()-o;if(d<100)continue;let l=null,r="";s.type==="user.message"&&n.type==="assistant.turn_start"?(l="input-consumption",r=`LLM reading user input (${(s.data?.message||"").length} chars)`):s.type==="assistant.turn_start"&&n.type==="assistant.message"?(l="llm-generation",r=`LLM generating response (${(n.data?.content||"").length} chars output)`):s.type==="assistant.turn_start"&&n.type==="tool.execution_start"?(l="llm-generation",r=`LLM deciding to call ${n.data?.toolName||"unknown"}`):s.type==="assistant.message"&&n.type==="assistant.turn_start"?(l="turn-gap",r="Gap between assistant response and next turn"):s.type==="tool.execution_complete"&&d>500?(l="post-tool",r=`Processing ${s.data?.toolName||"unknown"} result`):d>5e3&&(l="idle",r=`${s.type} \u2192 ${n.type}`),l&&t.push({type:l,description:r,startTime:s.timestamp,endTime:n.timestamp,duration:d,fromEvent:s.type,toEvent:n.type,fromData:s.data,toData:n.data})}return t.sort((a,s)=>(s.duration||0)-(a.duration||0))}),Oe=v(()=>Math.max(...ae.value.map(e=>e.duration||0),1)),je=v(()=>{let e={"input-consumption":{count:0,total:0,avg:0},"llm-generation":{count:0,total:0,avg:0},"post-tool":{count:0,total:0,avg:0},"turn-gap":{count:0,total:0,avg:0},idle:{count:0,total:0,avg:0}};return ae.value.forEach(t=>{e[t.type]&&(e[t.type].count++,e[t.type].total+=t.duration)}),Object.keys(e).forEach(t=>{e[t].count>0&&(e[t].avg=e[t].total/e[t].count)}),e}),$e=v(()=>{let e=x.value.length;if(e===0)return 100;let t=x.value.filter(a=>a.isError).length;return((e-t)/e*100).toFixed(1)}),Ue=v(()=>x.value.filter(e=>e.isError).length),Ve=v(()=>{let e=S.value,t=0;for(let o=0;o<e.length-1;o++){let i=e[o],d=e[o+1];if(d.type==="user.message"&&i.type!=="user.message"){let l=new Date(d.timestamp).getTime()-new Date(i.timestamp).getTime();l>1e3&&(t+=l)}}let a=_.value||0,s=Math.max(a-t,0),n=Math.max(s-se.value,0);return{userThinkingTime:t,agentWorkingTime:s,llmTime:n,userThinkingPct:a>0?(t/a*100).toFixed(0):0,agentWorkingPct:a>0?(s/a*100).toFixed(0):0,llmPct:a>0?(n/a*100).toFixed(0):0,toolPct:a>0?(se.value/a*100).toFixed(0):0}}),ne=v(()=>x.value.length),We=v(()=>ne.value?x.value.reduce((t,a)=>t+(a.duration||0),0)/ne.value:0),He=v(()=>x.value.length?x.value.reduce((e,t)=>(t.duration||0)>(e.duration||0)?t:e):null),oe=v(()=>{let e=S.value;if(e.some(n=>n.data?.source==="vscode"))return!0;let a=e.some(n=>n.type==="assistant.message"&&n.data?.subAgentName),s=e.some(n=>n.type==="subagent.started"||n.type==="subagent.completed"||n.type==="subagent.failed");return a&&!s}),we=v(()=>{if(!oe.value)return[];let e=S.value,t=new Map;for(let a=0;a<e.length;a++){let s=e[a];if(s.type==="assistant.message"&&s.data?.subAgentName){let n=s.data.subAgentId||s.data.subAgentName;t.has(n)||t.set(n,{name:s.data.subAgentName,events:[],toolCount:0,firstIndex:a,status:"completed",subAgentId:s.data.subAgentId});let o=t.get(n);o.events.push(s),s.data.tools&&Array.isArray(s.data.tools)&&(o.toolCount+=s.data.tools.length),(s.data.error||s.data.status==="error")&&(o.status="failed")}}return Array.from(t.values()).sort((a,s)=>a.firstIndex-s.firstIndex)}),ie=v(()=>{let e=[],t=ye.value,a=B.value,s=S.value;if(oe.value){let n=we.value,o=[];for(let i=0;i<s.length;i++)s[i].type==="user.message"&&o.push({event:s[i],sortedIndex:i});if(o.length>0&&n.length>0)for(let i=0;i<o.length;i++){let{event:d,sortedIndex:l}=o[i],r=o[i+1]?o[i+1].sortedIndex:1/0,c=n.filter(u=>u.firstIndex>=l&&u.firstIndex<r),m=c.reduce((u,g)=>u+g.toolCount,0),p=d.data?.message||d.data?.content||"";e.push({rowType:"user-req",userReqNumber:i+1,message:typeof p=="string"?p.substring(0,120):String(p).substring(0,120),toolCount:m,sequenceIndex:l,isSequenceEstimated:!0,duration:m});for(let u of c)e.push({rowType:"subagent",itemType:"subagent",name:u.name,status:u.status,toolCount:u.toolCount,sequenceIndex:u.firstIndex,isSequenceEstimated:!0,duration:u.toolCount,indented:!0})}else if(n.length>0)for(let i of n)e.push({rowType:"subagent",itemType:"subagent",name:i.name,status:i.status,toolCount:i.toolCount,sequenceIndex:i.firstIndex,isSequenceEstimated:!0,duration:i.toolCount});return e}if(t.length)for(let n=0;n<t.length;n++){let o=t[n],i=o.turns;if(!i.length)continue;let d=new Date(i[0].startTime).getTime(),l=new Date(i[i.length-1].endTime).getTime();e.push({rowType:"user-req",userReqNumber:o.userReqNumber,message:o.message,startTime:i[0].startTime,endTime:i[i.length-1].endTime,duration:l-d});let r=a.filter(c=>{if(!c.startTime)return!1;let m=new Date(c.startTime).getTime();return m>=d&&m<=l});if(r.length)for(let c=0;c<r.length;c++){let m=r[c],p=c===0?d:new Date(r[c-1].endTime).getTime(),u=new Date(m.startTime).getTime();if(u-p>500){let g=F(s,p,u);g.rowType="main-agent",e.push(g)}if(e.push({...m,rowType:"subagent",itemType:"subagent"}),c===r.length-1){let g=new Date(m.endTime).getTime(),T=l;if(T-g>500){let f=F(s,g,T);f.rowType="main-agent",e.push(f)}}}else if(l-d>0){let c=F(s,d,l);c.rowType="main-agent",e.push(c)}}else if(ee.value.length)for(let n of ee.value)e.push({...n,rowType:n.itemType==="agent-op"?"main-agent":"subagent"});return e}),Ye=(e,t)=>{if(!A.value||!_.value||!e)return{left:"0%",width:"0%"};let a=new Date(e).getTime(),s=t?new Date(t).getTime():a+1e3,n=(a-A.value)/_.value*100,o=Math.max((s-a)/_.value*100,.5);return{left:n+"%",width:Math.min(o,100-n)+"%"}},re=e=>{let t=ie.value;if(t.length===0)return{left:"0%",width:"0%"};if(e.rowType==="user-req"){let c=t.findIndex(f=>f===e);if(c===-1)return{left:"0%",width:"0%"};let m=[];for(let f=c+1;f<t.length&&t[f].rowType!=="user-req";f++)t[f].rowType==="subagent"&&m.push(t[f]);if(m.length===0){let w=t.filter(k=>k.rowType!=="user-req").reduce((k,y)=>k+(y.toolCount||0),0);if(w===0)return{left:"0%",width:"0%"};let q=0;for(let k=0;k<c;k++)t[k].rowType==="subagent"&&(q+=t[k].toolCount||0);return{left:q/w*100+"%",width:Math.max(1,1/w*100)+"%"}}let p=re(m[0]),u=re(m[m.length-1]),g=parseFloat(p.left),T=parseFloat(u.left)+parseFloat(u.width);return{left:g+"%",width:T-g+"%"}}if(t.findIndex(c=>c===e)===-1)return{left:"0%",width:"0%"};let s=t.filter(c=>c.rowType!=="user-req"),n=s.reduce((c,m)=>c+(m.toolCount||0),0);if(n===0)return{left:"0%",width:"0%"};let o=s.findIndex(c=>c===e);if(o===-1)return{left:"0%",width:"0%"};let i=0;for(let c=0;c<o;c++)i+=s[c].toolCount||0;let d=i/n*100,l=e.toolCount||0,r=Math.max(l/n*100,2);return{left:d+"%",width:Math.min(r,100-d)+"%"}},Ze=e=>{I.value===e?M.value=M.value==="asc"?"desc":"asc":(I.value=e,M.value=e==="duration"?"desc":"asc")},Xe=e=>I.value!==e?"\u2195":M.value==="asc"?"\u2191":"\u2193",Ke=e=>{let t=(e||"").toLowerCase();return["bash","exec"].includes(t)?"badge-bash":t==="read"?"badge-read":t==="write"||t==="notebookedit"?"badge-write":t==="edit"?"badge-edit":t==="glob"||t==="grep"?"badge-search":t==="task"?"badge-subagent":"badge-other"},Je=e=>({read:"badge-read",write:"badge-write",edit:"badge-edit",create:"badge-create",search:"badge-search"})[e]||"badge-other";Ce(async()=>{try{let e=await fetch("/api/sessions/"+C.value+"/events");if(!e.ok)throw new Error("Failed to load events: "+e.statusText);let t=await e.json();console.log("[TIME-ANALYZE] Loaded events:",t.length),console.log("[TIME-ANALYZE] Event types:",[...new Set(t.map(a=>a.type))]),console.log("[TIME-ANALYZE] Turn starts:",t.filter(a=>a.type==="assistant.turn_start").length),console.log("[TIME-ANALYZE] User messages:",t.filter(a=>a.type==="user.message").length),b.value=t.sort((a,s)=>{let n=a.timestamp?new Date(a.timestamp).getTime():0,o=s.timestamp?new Date(s.timestamp).getTime():0;return n!==o?n-o:(a._fileIndex??0)-(s._fileIndex??0)}),console.log("[TIME-ANALYZE] Events set, length:",b.value.length)}catch(e){console.error("[TIME-ANALYZE] Error loading events:",e),Z.value=e.message}finally{E.value=!1}});let le=h("not_started"),ce=h(null),H=h(null),de=h(0),Y=null,Qe=v(()=>L.value?marked.parse(L.value):""),xe=async()=>{try{let t=await(await fetch(`/session/${C.value}/insight`)).json();le.value=t.status,t.status==="completed"?(L.value=t.report,P.value=null,J.value=t.generatedAt,me()):t.status==="generating"?(P.value=t.log||null,H.value=t.startedAt,ce.value=t.lastUpdate,de.value=t.ageMs,ue(),Vue.nextTick(()=>{let a=document.getElementById("insight-log");a&&(a.scrollTop=a.scrollHeight)})):t.status==="timeout"&&(P.value=t.log||null,H.value=t.startedAt,ce.value=t.lastUpdate,de.value=t.ageMs,ue())}catch(e){console.error("Failed to check insight:",e)}},ue=()=>{me(),Y=setInterval(xe,2e3)},me=()=>{Y&&(clearInterval(Y),Y=null)},ke=async(e=!1)=>{X.value=!0,K.value=null,P.value=null;try{let t=await fetch(`/session/${C.value}/insight`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({force:e})});if(!t.ok){let s=await t.json();throw new Error(s.error||"Failed to generate insight")}let a=await t.json();le.value=a.status,a.status==="generating"?(H.value=a.startedAt,ue()):a.status==="completed"&&(L.value=a.report,J.value=a.generatedAt)}catch(t){K.value=t.message}finally{X.value=!1}},et=async()=>{await ke(!0)};return Ce(async()=>{await xe()}),at(()=>{me()}),{sessionId:C,metadata:fe,events:b,loading:E,error:Z,activeTab:Ae,sortField:I,sortDir:M,insightReport:L,insightLog:P,insightLoading:X,insightError:K,insightGeneratedAt:J,insightStatus:le,insightLastUpdate:ce,insightStartedAt:H,insightAgeMs:de,renderedInsight:Qe,generateInsight:ke,regenerateInsight:et,formatDuration:W,formatTime:qe,formatDateTime:Ne,sessionStart:A,sessionEnd:G,totalDuration:_,subagentAnalysis:B,maxSubagentDuration:Re,subagentTimelineItems:ee,subagentStats:Le,EVENT_MARKER_CATEGORIES:D,showMarkerLegend:Ee,copyLabel:$,copyTimelineMarkdown:_e,ganttCrosshairX:U,ganttCrosshairTime:he,onGanttMouseMove:Ie,onGanttMouseLeave:De,turnAnalysis:te,maxTurnDuration:Pe,groupedTurns:ye,unifiedTimelineItems:ie,toolAnalysis:x,sortedToolAnalysis:Ge,maxToolDuration:ze,fileOperations:be,fileStats:Be,toolTimeByCategory:Te,maxCategoryTime:Fe,totalToolTime:se,totalToolCount:ne,avgToolDuration:We,longestTool:He,successRate:$e,errorCount:Ue,timeBreakdown:Ve,gapAnalysis:ae,maxGapDuration:Oe,gapStats:je,ganttPosition:Ye,ganttSequencePosition:re,toggleSort:Ze,sortIcon:Xe,getToolBadgeClass:Ke,getOpBadgeClass:Je,isVSCodeSession:oe,vsCodeSubagents:we}},template:`
3
3
  <div v-if="loading" class="empty-state" style="padding: 60px;">
4
4
  \u23F3 Loading events...
5
5
  </div>
package/views/index.ejs CHANGED
@@ -175,6 +175,7 @@
175
175
  -webkit-line-clamp: 3;
176
176
  -webkit-box-orient: vertical;
177
177
  overflow: hidden;
178
+ cursor: help;
178
179
  }
179
180
  .session-divider {
180
181
  height: 1px;
@@ -355,6 +356,15 @@
355
356
  font-weight: 600;
356
357
  font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
357
358
  }
359
+ .status-badge.source-modernize {
360
+ padding: 2px 8px;
361
+ background: rgba(76, 175, 80, 0.15);
362
+ color: #66bb6a;
363
+ border-radius: 12px;
364
+ font-size: 11px;
365
+ font-weight: 600;
366
+ font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
367
+ }
358
368
 
359
369
  /* Filter pills */
360
370
  .filter-pills {
@@ -519,6 +529,7 @@
519
529
  <button class="filter-pill active" data-source="copilot">Copilot CLI</button>
520
530
  <button class="filter-pill" data-source="vscode">Copilot Chat</button>
521
531
  <button class="filter-pill" data-source="claude">Claude</button>
532
+ <button class="filter-pill" data-source="modernize">Modernize CLI</button>
522
533
  <button class="filter-pill" data-source="pi-mono">Pi</button>
523
534
  </div>
524
535
  <p class="hint source-hint" id="sourceHint"></p>
@@ -562,6 +573,248 @@
562
573
  .date-group-header:first-child {
563
574
  margin-top: 0;
564
575
  }
576
+
577
+ /* Custom summary tooltip */
578
+ .summary-tooltip {
579
+ display: none;
580
+ position: fixed;
581
+ z-index: 9999;
582
+ background: #1c2128;
583
+ border: 1px solid #30363d;
584
+ border-radius: 8px;
585
+ padding: 12px 16px;
586
+ max-width: 600px;
587
+ width: max-content;
588
+ max-height: 400px;
589
+ overflow-y: auto;
590
+ font-size: 13px;
591
+ line-height: 1.6;
592
+ color: #c9d1d9;
593
+ white-space: normal;
594
+ word-break: break-word;
595
+ box-shadow: 0 8px 24px rgba(0,0,0,0.5);
596
+ pointer-events: none;
597
+ }
598
+ .summary-tooltip.visible {
599
+ display: block;
600
+ pointer-events: auto;
601
+ }
602
+ /* Markdown prose styles inside tooltip */
603
+ .summary-tooltip h1, .summary-tooltip h2, .summary-tooltip h3,
604
+ .summary-tooltip h4, .summary-tooltip h5, .summary-tooltip h6 {
605
+ color: #e6edf3; margin: 8px 0 4px; font-weight: 600;
606
+ }
607
+ .summary-tooltip h1 { font-size: 15px; }
608
+ .summary-tooltip h2 { font-size: 14px; }
609
+ .summary-tooltip h3, .summary-tooltip h4 { font-size: 13px; }
610
+ .summary-tooltip p { margin: 4px 0; }
611
+ .summary-tooltip ul, .summary-tooltip ol { margin: 4px 0; padding-left: 18px; }
612
+ .summary-tooltip li { margin: 2px 0; }
613
+ .summary-tooltip code {
614
+ background: #2d333b; border-radius: 3px;
615
+ padding: 1px 4px; font-size: 12px; font-family: monospace;
616
+ }
617
+ .summary-tooltip pre {
618
+ background: #2d333b; border-radius: 6px;
619
+ padding: 8px 10px; overflow-x: auto; margin: 6px 0;
620
+ }
621
+ .summary-tooltip pre code { background: none; padding: 0; }
622
+ .summary-tooltip strong { color: #e6edf3; }
623
+ .summary-tooltip hr { border-color: #30363d; margin: 8px 0; }
624
+
625
+ /* Hide hover tooltip on touch devices */
626
+ @media (hover: none) {
627
+ .summary-tooltip { display: none !important; }
628
+ }
629
+ /* Bottom sheet for mobile long-press */
630
+ .bottom-sheet-overlay {
631
+ display: none;
632
+ position: fixed;
633
+ inset: 0;
634
+ background: rgba(0,0,0,0.5);
635
+ z-index: 1000;
636
+ touch-action: none;
637
+ }
638
+ .bottom-sheet-overlay.visible { display: block; }
639
+ .bottom-sheet {
640
+ position: fixed;
641
+ bottom: 0;
642
+ left: 0;
643
+ right: 0;
644
+ background: #161b22;
645
+ border-top: 1px solid #30363d;
646
+ border-radius: 16px 16px 0 0;
647
+ padding: 0 16px max(env(safe-area-inset-bottom, 0px), 24px);
648
+ max-height: 70vh;
649
+ overflow-y: auto;
650
+ z-index: 1001;
651
+ transform: translateY(100%);
652
+ transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
653
+ }
654
+ .bottom-sheet.visible { transform: translateY(0); }
655
+ .bottom-sheet-handle {
656
+ width: 36px;
657
+ height: 4px;
658
+ background: #444c56;
659
+ border-radius: 2px;
660
+ margin: 12px auto 16px;
661
+ }
662
+ .bottom-sheet-content {
663
+ font-size: 14px;
664
+ line-height: 1.6;
665
+ color: #c9d1d9;
666
+ }
667
+ .bottom-sheet-content h1, .bottom-sheet-content h2, .bottom-sheet-content h3 { color: #e6edf3; margin: 12px 0 6px; }
668
+ .bottom-sheet-content h1 { font-size: 16px; }
669
+ .bottom-sheet-content h2 { font-size: 15px; }
670
+ .bottom-sheet-content h3 { font-size: 14px; }
671
+ .bottom-sheet-content p { margin: 6px 0; }
672
+ .bottom-sheet-content ul, .bottom-sheet-content ol { margin: 6px 0; padding-left: 20px; }
673
+ .bottom-sheet-content li { margin: 3px 0; }
674
+ .bottom-sheet-content code { background: #0d1117; padding: 1px 5px; border-radius: 4px; font-size: 13px; }
675
+ .bottom-sheet-content pre { background: #0d1117; padding: 10px; border-radius: 6px; overflow-x: auto; }
676
+ .bottom-sheet-content pre code { background: none; padding: 0; }
677
+ .bottom-sheet-content strong { color: #e6edf3; }
678
+ .bottom-sheet-content hr { border-color: #30363d; margin: 10px 0; }
565
679
  </style>
680
+
681
+ <script src="https://cdn.jsdelivr.net/npm/marked@9/marked.min.js"></script>
682
+ <div id="summary-tooltip" class="summary-tooltip"></div>
683
+
684
+ <script>
685
+ const tooltip = document.getElementById('summary-tooltip');
686
+ let tooltipTarget = null;
687
+ let hideTimer = null;
688
+ let showTimer = null;
689
+ let pendingEl = null;
690
+ let pendingEvent = null;
691
+
692
+ function showTooltip(el, e) {
693
+ clearTimeout(hideTimer);
694
+ tooltipTarget = el;
695
+ if (!el.dataset.tooltipText) {
696
+ el.dataset.tooltipText = el.getAttribute('title');
697
+ el.removeAttribute('title');
698
+ }
699
+ const md = el.dataset.tooltipText || '';
700
+ tooltip.innerHTML = (typeof marked !== 'undefined')
701
+ ? marked.parse(md, { breaks: true })
702
+ : md.replace(/</g, '&lt;').replace(/\n/g, '<br>');
703
+ tooltip.classList.add('visible');
704
+ positionTooltip(e);
705
+ }
706
+
707
+ function scheduleShow(el, e) {
708
+ clearTimeout(showTimer);
709
+ pendingEl = el;
710
+ pendingEvent = e;
711
+ showTimer = setTimeout(() => {
712
+ if (pendingEl) showTooltip(pendingEl, pendingEvent);
713
+ }, 500);
714
+ }
715
+
716
+ function cancelShow() {
717
+ clearTimeout(showTimer);
718
+ pendingEl = null;
719
+ }
720
+
721
+ function scheduleHide() {
722
+ cancelShow();
723
+ hideTimer = setTimeout(() => {
724
+ tooltipTarget = null;
725
+ tooltip.classList.remove('visible');
726
+ }, 120);
727
+ }
728
+
729
+ document.addEventListener('mouseover', e => {
730
+ const el = e.target.closest('.session-summary');
731
+ if (el) { clearTimeout(hideTimer); scheduleShow(el, e); return; }
732
+ if (e.target.closest('#summary-tooltip')) { clearTimeout(hideTimer); cancelShow(); return; }
733
+ });
734
+
735
+ document.addEventListener('mousemove', e => {
736
+ if (pendingEl) { pendingEvent = e; }
737
+ if (!tooltipTarget) return;
738
+ if (e.target.closest('#summary-tooltip')) return;
739
+ positionTooltip(e);
740
+ });
741
+
742
+ document.addEventListener('mouseout', e => {
743
+ const toEl = e.relatedTarget;
744
+ if (toEl && (toEl.closest('.session-summary') || toEl.closest('#summary-tooltip'))) return;
745
+ scheduleHide();
746
+ });
747
+
748
+ tooltip.addEventListener('mouseleave', () => scheduleHide());
749
+
750
+ function positionTooltip(e) {
751
+ const pad = 14;
752
+ const tw = tooltip.offsetWidth;
753
+ const th = tooltip.offsetHeight;
754
+ let x = e.clientX + pad;
755
+ let y = e.clientY + pad;
756
+ if (x + tw > window.innerWidth - 8) x = e.clientX - tw - pad;
757
+ if (y + th > window.innerHeight - 8) y = e.clientY - th - pad;
758
+ tooltip.style.left = x + 'px';
759
+ tooltip.style.top = y + 'px';
760
+ }
761
+ </script>
762
+
763
+ <!-- Bottom sheet for mobile long-press summary -->
764
+ <div id="sheet-overlay" class="bottom-sheet-overlay">
765
+ <div id="bottom-sheet" class="bottom-sheet">
766
+ <div class="bottom-sheet-handle"></div>
767
+ <div id="sheet-content" class="bottom-sheet-content"></div>
768
+ </div>
769
+ </div>
770
+
771
+ <script>
772
+ const sheetOverlay = document.getElementById('sheet-overlay');
773
+ const bottomSheet = document.getElementById('bottom-sheet');
774
+ const sheetContent = document.getElementById('sheet-content');
775
+
776
+ function openSheet(md) {
777
+ sheetContent.innerHTML = (typeof marked !== 'undefined')
778
+ ? marked.parse(md, { breaks: true })
779
+ : md.replace(/</g, '&lt;').replace(/\n/g, '<br>');
780
+ sheetOverlay.classList.add('visible');
781
+ requestAnimationFrame(() => bottomSheet.classList.add('visible'));
782
+ }
783
+
784
+ function closeSheet() {
785
+ bottomSheet.classList.remove('visible');
786
+ setTimeout(() => sheetOverlay.classList.remove('visible'), 280);
787
+ }
788
+
789
+ sheetOverlay.addEventListener('click', e => {
790
+ if (!bottomSheet.contains(e.target)) closeSheet();
791
+ });
792
+
793
+ // Long-press detection (500ms)
794
+ let lpTimer = null;
795
+ let lpMoved = false;
796
+
797
+ document.addEventListener('touchstart', e => {
798
+ const el = e.target.closest('.session-summary');
799
+ if (!el) return;
800
+ lpMoved = false;
801
+ const md = el.dataset.tooltipText || el.getAttribute('title') || '';
802
+ if (!md) return;
803
+ lpTimer = setTimeout(() => {
804
+ if (!lpMoved) {
805
+ e.preventDefault();
806
+ openSheet(md);
807
+ }
808
+ }, 500);
809
+ }, { passive: false });
810
+
811
+ document.addEventListener('touchmove', () => {
812
+ lpMoved = true;
813
+ clearTimeout(lpTimer);
814
+ }, { passive: true });
815
+
816
+ document.addEventListener('touchend', () => clearTimeout(lpTimer), { passive: true });
817
+ document.addEventListener('touchcancel', () => clearTimeout(lpTimer), { passive: true });
818
+ </script>
566
819
  </body>
567
820
  </html>