@qiaolei81/copilot-session-viewer 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/server.min.js +45 -40
- package/package.json +10 -8
- package/public/js/homepage.min.js +9 -9
- package/public/js/session-detail.min.js +105 -7
- package/views/index.ejs +243 -0
- package/views/session-vue.ejs +140 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qiaolei81/copilot-session-viewer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Web UI for viewing GitHub Copilot CLI session logs",
|
|
5
5
|
"author": "Lei Qiao <qiaolei81@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "node scripts/build.mjs",
|
|
35
35
|
"build:watch": "node scripts/build.mjs --watch",
|
|
36
|
-
"start": "node server.js",
|
|
37
|
-
"dev": "nodemon server.js",
|
|
36
|
+
"start": "cross-env DISABLE_TELEMETRY=true node server.js",
|
|
37
|
+
"dev": "cross-env DISABLE_TELEMETRY=true nodemon server.js",
|
|
38
38
|
"test": "jest",
|
|
39
39
|
"test:watch": "jest --watch",
|
|
40
40
|
"test:coverage": "jest --coverage",
|
|
41
|
-
"test:e2e": "playwright test",
|
|
42
|
-
"test:e2e:headed": "playwright test --headed",
|
|
43
|
-
"test:e2e:debug": "playwright test --debug",
|
|
41
|
+
"test:e2e": "cross-env DISABLE_TELEMETRY=true playwright test",
|
|
42
|
+
"test:e2e:headed": "cross-env DISABLE_TELEMETRY=true playwright test --headed",
|
|
43
|
+
"test:e2e:debug": "cross-env DISABLE_TELEMETRY=true playwright test --debug",
|
|
44
44
|
"test:all": "npm test && npm run test:e2e",
|
|
45
45
|
"test:coverage:all": "npm run test:coverage && npm run test:e2e",
|
|
46
46
|
"lint": "eslint .",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"prepublishOnly": "npm run build:prod"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
+
"adm-zip": "^0.5.16",
|
|
57
58
|
"applicationinsights": "^2.9.8",
|
|
58
59
|
"compression": "^1.8.1",
|
|
59
60
|
"dompurify": "^3.3.1",
|
|
@@ -61,9 +62,9 @@
|
|
|
61
62
|
"express": "^4.18.2",
|
|
62
63
|
"express-rate-limit": "^8.2.1",
|
|
63
64
|
"helmet": "^8.1.0",
|
|
65
|
+
"js-yaml": "^4.1.1",
|
|
64
66
|
"multer": "^2.0.2",
|
|
65
|
-
"zod": "^4.3.6"
|
|
66
|
-
"adm-zip": "^0.5.16"
|
|
67
|
+
"zod": "^4.3.6"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@eslint/css": "^0.14.1",
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
74
75
|
"@playwright/test": "^1.58.2",
|
|
75
76
|
"@types/jest": "^30.0.0",
|
|
77
|
+
"cross-env": "^10.1.0",
|
|
76
78
|
"esbuild": "^0.27.3",
|
|
77
79
|
"eslint": "^10.0.0",
|
|
78
80
|
"eslint-plugin-vue": "^10.8.0",
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
(()=>{var
|
|
1
|
+
(()=>{var y=window.__PAGE_DATA||{},A=y.sessions||[],G=y.totalSessions||0,D=y.hasMore||!1,k=y.sourceHints||{},m=[...A],u={};u.copilot={offset:A.length,hasMore:D};var f=!1,S="sessionViewer.sourceFilter",$;try{$=localStorage.getItem(S)}catch{$=null}var i=$||"copilot";function g(){return u[i]||(u[i]={offset:0,hasMore:!0}),u[i]}async function B(){let e=g();if(f||!e.hasMore)return;f=!0;let t=document.getElementById("loading-indicator");t.style.display="block",window.trackClick("LoadMoreClicked",{currentPage:Math.floor(e.offset/20)+1,offset:e.offset,source:i});try{let s=await fetch(`/api/sessions/load-more?offset=${g().offset}&limit=20&source=${encodeURIComponent(i)}`);if(!s.ok)throw new Error("Failed to load more sessions");let o=await s.json(),n=new Set(m.map(r=>r.id)),a=[];for(let r of o.sessions)n.has(r.id)||(m.push(r),a.push(r));g().offset+=o.sessions.length,g().hasMore=o.hasMore,await C(a),E()}catch(s){console.error("Error loading more sessions:",s)}finally{f=!1,t.style.display="none"}}function F(){return m.filter(e=>e.source===i)}function E(){let e=document.getElementById("sessions-container");e.innerHTML="";let t=F();if(t.length===0){e.innerHTML='<div style="text-align: center; color: #6e7681; padding: 40px; font-size: 14px;">No sessions found for this filter.</div>';return}let s=O(t);Object.keys(s).sort((n,a)=>a.localeCompare(n)).forEach(n=>{let a=document.createElement("div");a.className="date-group-header",a.textContent=z(s[n][0].createdAt),e.appendChild(a);let r=document.createElement("div");r.className="recent-list",s[n].forEach(h=>{r.innerHTML+=R(h)}),e.appendChild(r)})}function P(){let e=window.pageYOffset||document.documentElement.scrollTop,t=window.innerHeight,s=document.documentElement.scrollHeight;e+t>=s-500&&g().hasMore&&!f&&B()}var w;function Z(){w||(w=setTimeout(()=>{P(),w=null},100))}function _(e){e.preventDefault();let t=document.getElementById("sessionInput").value.trim();t&&(window.location.href=`/session/${t}`)}document.getElementById("sessionForm").addEventListener("submit",_);var M=document.getElementById("fileInput"),d=document.getElementById("importLink"),L=document.getElementById("importStatus");d.addEventListener("click",e=>{e.preventDefault(),M.click()});M.addEventListener("change",async e=>{let t=e.target.files[0];if(t){if(!t.name.endsWith(".zip")){p("error","\u274C Please select a .zip file");return}d.style.pointerEvents="none",d.style.opacity="0.5",d.textContent="Importing...",p("loading","Uploading and extracting session...");try{let s=new FormData;s.append("sessionZip",t);let o=await fetch("/session/import",{method:"POST",body:s}),n=await o.json();o.ok?(p("success",`\u2705 Session ${n.sessionId} imported successfully!`),setTimeout(()=>{window.location.reload()},1500)):(p("error",`\u274C Import failed: ${n.error}`),d.style.pointerEvents="auto",d.style.opacity="1",d.textContent="Import session from zip")}catch(s){p("error",`\u274C Import failed: ${s.message}`),d.style.pointerEvents="auto",d.style.opacity="1",d.textContent="Import session from zip"}finally{M.value=""}}});function p(e,t){L.className=`import-status ${e}`,L.textContent=t}function N(e){if(!e||e<0)return"\u2014";let t=Math.floor(e/1e3),s=Math.floor(t/60),o=Math.floor(s/60);if(o>0){let n=s%60;return`${o}h ${n}m`}else if(s>0){let n=t%60;return`${s}m ${n}s`}else return`${t}s`}function z(e){let t=new Date(e),s=t.getFullYear(),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${s}/${o}/${n}`}function j(e){if(!e)return"Unknown";let t=new Date(e),s=t.getFullYear(),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${s}-${o}-${n}`}function O(e){let t={};return e.forEach(s=>{let o=j(s.createdAt);t[o]||(t[o]=[]),t[o].push(s)}),t}function R(e){let t="",s=e.sourceBadgeClass||"source-copilot",o=e.sourceName||"Copilot";if(t+=`<span class="status-badge ${s}" title="${o}">${o}</span>`,e.sessionStatus==="wip"&&(t+='<span class="status-badge wip" title="Session in progress">\u{1F504} WIP</span>'),e.isImported&&(t+='<span class="status-badge imported" title="Imported session">\u{1F4E5}</span>'),e.hasInsight&&(t+='<span class="status-badge insight" title="Has Agent Review">\u{1F4A1}</span>'),e.selectedModel){let v=e.selectedModel.replace("claude-","").replace("gpt-","").replace("gemini-",""),l="model-other";e.selectedModel.includes("claude")?l="model-claude":e.selectedModel.includes("gpt")?l="model-gpt":e.selectedModel.includes("gemini")&&(l="model-gemini"),t+=`<span class="status-badge model ${l}" title="Model: ${c(e.selectedModel)}">${c(v)}</span>`}e.copilotVersion&&(t+=`<span class="status-badge version" title="CLI version">${c(e.copilotVersion)}</span>`);let n="";if(e.summary&&e.summary!=="No summary"&&e.summary!=="Legacy session"){let v=e.summary.replace(/"/g,"""),l=c(e.summary).replace(/\n+/g," ");n=`<div class="session-summary" title="${v}">${l}</div>`}else n='<div class="session-summary" style="color: #6e7681; font-style: italic;">No summary available</div>';let a="";e.workspace&&e.workspace.cwd&&(a=`
|
|
2
2
|
<div class="session-info-item workspace" title="${c(e.workspace.cwd)}">
|
|
3
3
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M1.75 1A1.75 1.75 0 000 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0016 13.25v-8.5A1.75 1.75 0 0014.25 3H7.5a.25.25 0 01-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75z"></path></svg>
|
|
4
|
-
<span class="session-info-value">${c(e.workspace.cwd
|
|
4
|
+
<span class="session-info-value">${c(e.workspace.cwd)}</span>
|
|
5
5
|
</div>
|
|
6
|
-
`);let r=e.createdAt?new Date(e.createdAt).toLocaleString("en-US",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1}):"unknown",
|
|
6
|
+
`);let r=e.createdAt?new Date(e.createdAt).toLocaleString("en-US",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1}):"unknown",h="";e.duration&&(h=`
|
|
7
7
|
<div class="session-info-item">
|
|
8
8
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM7.25 12.5v-5A.75.75 0 0 1 8 6.75h2.5a.75.75 0 0 1 0 1.5H8.75v4.25a.75.75 0 0 1-1.5 0Z"></path></svg>
|
|
9
|
-
<span class="session-info-value">${
|
|
9
|
+
<span class="session-info-value">${N(e.duration)}</span>
|
|
10
10
|
</div>
|
|
11
|
-
`);let
|
|
12
|
-
<a href="/session/${e.id}" class="recent-item${
|
|
11
|
+
`);let T=e.sessionStatus==="wip"?" recent-item-wip":"",I="";return e.tags&&e.tags.length>0&&(I=`<div class="session-tags">${e.tags.map(l=>`<span class="session-tag" style="background-color: ${U(l)}" title="${c(l)}">${c(l)}</span>`).join("")}</div>`),`
|
|
12
|
+
<a href="/session/${e.id}" class="recent-item${T}" onclick="trackClick('SessionCardClicked', { sessionId: '${c(e.id)}', source: '${c(e.source||"unknown")}' })">
|
|
13
13
|
<div class="session-id">
|
|
14
14
|
<span class="session-id-text" title="${c(e.id)}">${c(e.id)}</span>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="session-badges-tags">
|
|
17
17
|
<div class="session-badges">${t}</div>
|
|
18
|
-
${
|
|
18
|
+
${I}
|
|
19
19
|
</div>
|
|
20
20
|
${n}
|
|
21
21
|
<div class="session-divider"></div>
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7-3.25v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5a.75.75 0 0 1 1.5 0Z"></path></svg>
|
|
26
26
|
<span class="session-info-value">${r}</span>
|
|
27
27
|
</div>
|
|
28
|
-
${
|
|
28
|
+
${h}
|
|
29
29
|
<div class="session-info-item">
|
|
30
30
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M7.72.72a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 0 1-1.06 1.06l-.22-.22v1.69a.75.75 0 0 1-1.5 0V3.06l-.22.22a.75.75 0 0 1-1.06-1.06ZM2 7a.75.75 0 0 0 0 1.5h3.69l-.22.22a.75.75 0 1 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06l-1.5-1.5a.75.75 0 0 0-1.06 1.06l.22.22Zm8.53-.28a.75.75 0 0 0 0 1.06l1.5 1.5a.75.75 0 1 0 1.06-1.06l-.22-.22H16a.75.75 0 0 0 0-1.5h-3.13l.22-.22a.75.75 0 0 0-1.06-1.06ZM7.72 12.22a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 1 1-1.06 1.06l-.22-.22v1.69a.75.75 0 0 1-1.5 0v-1.69l-.22.22a.75.75 0 0 1-1.06-1.06Z"></path></svg>
|
|
31
31
|
<span class="session-info-value">${e.eventCount||0} events</span>
|
|
32
32
|
</div>
|
|
33
33
|
</div>
|
|
34
34
|
</a>
|
|
35
|
-
`}function c(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}var
|
|
35
|
+
`}function c(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}var H=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"];function U(e){let t=0;for(let s=0;s<e.length;s++)t=e.charCodeAt(s)+((t<<5)-t);return H[Math.abs(t)%H.length]}async function V(e){try{let t=e.map(n=>fetch(`/api/sessions/${n}/tags`).then(a=>a.ok?a.json():{tags:[]}).then(a=>({id:n,tags:a.tags||[]})).catch(()=>({id:n,tags:[]}))),s=await Promise.all(t),o={};return s.forEach(({id:n,tags:a})=>{o[n]=a}),o}catch(t){return console.error("Error loading session tags:",t),{}}}async function C(e){let t=e.map(o=>o.id),s=await V(t);e.forEach(o=>{o.tags=s[o.id]||[]})}function b(e){let t=document.getElementById("sourceHint");t&&k[e]?t.innerHTML='Sessions from <span class="hint-code">'+k[e]+"</span>":t&&(t.textContent="")}async function x(e){if(u[e]||(u[e]={offset:0,hasMore:!0}),u[e].offset===0&&!f){f=!0;let t=document.getElementById("sessions-container");t.innerHTML='<div style="text-align: center; color: #6e7681; padding: 40px; font-size: 14px;">\u23F3 Loading...</div>',document.getElementById("loading-indicator").style.display="none";try{let s=await fetch(`/api/sessions/load-more?offset=0&limit=20&source=${encodeURIComponent(e)}`);if(s.ok){let o=await s.json(),n=new Set(m.map(r=>r.id)),a=[];for(let r of o.sessions||[])n.has(r.id)||(m.push(r),a.push(r));u[e].offset=(o.sessions||[]).length,u[e].hasMore=o.hasMore,await C(a)}}catch(s){console.error("Failed to load sessions for source:",e,s)}finally{f=!1}}E()}function Y(){let e=document.querySelectorAll(".filter-pill");if(!new Set([...e].map(s=>s.getAttribute("data-source"))).has(i)){i="copilot";try{localStorage.setItem(S,i)}catch{}}e.forEach(s=>{s.classList.toggle("active",s.getAttribute("data-source")===i)}),b(i),e.forEach(s=>{s.addEventListener("click",async()=>{e.forEach(o=>o.classList.remove("active")),s.classList.add("active"),i=s.getAttribute("data-source");try{localStorage.setItem(S,i)}catch{}b(i),window.trackClick("FilterPillClicked",{pillName:s.textContent.trim(),dataSource:i}),await x(i)})})}document.addEventListener("DOMContentLoaded",async function(){await C(m),window.addEventListener("scroll",Z),Y(),i==="copilot"?E():await x(i)});})();
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
(()=>{(function(){if(typeof Vue>"u"){console.error("Vue is not loaded");return}if(typeof window.VueVirtualScroller>"u"){console.error("VueVirtualScroller is not loaded");return}console.log("Initializing Vue app...");let{createApp:
|
|
1
|
+
(()=>{(function(){if(typeof Vue>"u"){console.error("Vue is not loaded");return}if(typeof window.VueVirtualScroller>"u"){console.error("VueVirtualScroller is not loaded");return}console.log("Initializing Vue app...");let{createApp:fe,ref:u,computed:v,onMounted:X,onBeforeUnmount:be,watch:$}=Vue,{DynamicScroller:Te,DynamicScrollerItem:he}=window.VueVirtualScroller,Y=fe({components:{DynamicScroller:Te,DynamicScrollerItem:he},setup(){let m=u(window.__PAGE_DATA.sessionId),b=u(window.__PAGE_DATA.metadata),j=u(!1),Z=()=>window.innerWidth<=640,q=u(Z()?!0:localStorage.getItem("sidebarCollapsed")==="true");$(q,e=>{Z()||localStorage.setItem("sidebarCollapsed",e.toString())});let k=u({}),w=u({}),U=50,Q=()=>{let e=Object.keys(k.value);e.length>U&&e.slice(0,e.length-U).forEach(s=>delete k.value[s]);let t=Object.keys(w.value);t.length>U&&t.slice(0,t.length-U).forEach(s=>delete w.value[s])},E=u("all"),F=u(""),M=u(""),ee=u(0),g=u(null),te=u({start:0,end:0}),L=null;$(F,e=>{clearTimeout(L),L=setTimeout(()=>{M.value=e,e.trim()&&window.trackClick&&window.trackClick("SearchUsed",{query:e.substring(0,50),resultCount:N.value.length,sessionId:m.value})},300)}),$(E,()=>{Q()}),$(M,()=>{Q()});let C=u([]),ae=u(!0),se=u(null),T=v(()=>C.value.filter(t=>t.type!=="assistant.turn_end"&&t.type!=="assistant.turn_complete").sort((t,a)=>{let s=t.timestamp?new Date(t.timestamp).getTime():0,n=a.timestamp?new Date(a.timestamp).getTime():0;return s!==n?s-n:(t._fileIndex??0)-(a._fileIndex??0)}).map((t,a)=>({...t,virtualIndex:a,stableId:t.id||`${t.timestamp}-${t.type}-${a}`}))),ye=e=>{if(!M.value.trim())return!0;let t=M.value.toLowerCase();return[e.data?.message,e.data?.text,e.data?.content,e.data?.reason,e.data?.reasoningText,e.data?.errorType,e.data?.previousModel,e.data?.newModel].filter(Boolean).join(" ").toLowerCase().includes(t)},N=v(()=>{let e=a=>{let s=a.type||"";return s!=="tool.execution_start"&&s!=="tool.execution_complete"},t=T.value.filter(e);return M.value.trim()&&(t=t.filter(ye)),t}),A=v(()=>{let e=N.value;E.value!=="all"&&(e=e.filter(s=>s.type===E.value));let t=["assistant.turn_start","subagent.started","subagent.completed","subagent.failed"],a=e.length;return e.map((s,n)=>{let i=e[n+1],o=n===a-1,r=i&&t.includes(i.type);return{...s,filteredIndex:n,filteredTotal:a,isLastEvent:o||r}})}),xe=v(()=>{let e={};return N.value.forEach(t=>{t.type&&(e[t.type]=(e[t.type]||0)+1)}),e}),ke=v(()=>{if(!M.value.trim())return null;let e=N.value.length;return e>0?`${e} result${e!==1?"s":""}`:"No matches"}),we=v(()=>{let e=Object.keys(k.value).filter(a=>k.value[a]).length,t=Object.keys(w.value).filter(a=>w.value[a]).length;return e+t}),oe=v(()=>{let e=N.value.length,t=[{type:"all",label:`All (${e})`,count:e}],a={};N.value.forEach(n=>{n.type&&(a[n.type]=(a[n.type]||0)+1)});let s=Object.entries(a).sort((n,i)=>i[1]-n[1]).map(([n,i])=>({type:n,label:`${n} (${i})`,count:i,disabled:!1}));return[...t,...s]}),D=v(()=>{let e=T.value.filter(a=>a.type==="assistant.turn_start"),t=T.value.filter(a=>a.type==="user.message");return e.map((a,s)=>{let n=s,i=new Date(a.timestamp).getTime(),o,r=e.indexOf(a)+1;r<e.length?o=new Date(e[r].timestamp).getTime():o=Date.now();let c=o-i,l=Math.floor(c/1e3),d=Math.floor(l/60),p=l%60,J=d>0?`${d}m ${p}s`:`${p}s`,R=T.value.slice(0,T.value.indexOf(a)).reverse().find(f=>f.type==="user.message"),B=R?t.indexOf(R)+1:0;return{id:n,index:a.virtualIndex,originalTurnId:a.data?.turnId,timestamp:a.timestamp,duration:J,message:R?.data?.content||R?.data?.transformedContent||"",userReqNumber:B}})}),Ce=v(()=>{let e=[],t=new Map;return D.value.forEach(a=>{let s=a.userReqNumber||0;if(!t.has(s)){let n={reqNumber:s,message:a.message,turns:[]};t.set(s,n),e.push(n)}t.get(s).turns.push(a)}),e}),Se=(e,t)=>e?e.length<=t?e:e.substring(0,t)+"\u2026":"",Ie=v(()=>{let e=T.value,t=new Map,a=new Map,s=0;for(let o of e)if(o.type==="subagent.started"){let r=o.data?.toolCallId;r&&a.set(r,{name:o.data?.agentDisplayName||o.data?.agentName||"SubAgent",colorIndex:s++})}for(let o of e)if(o.type==="assistant.message"&&o.data?.subAgentName&&o.data?.subAgentId){let r=o.data.subAgentId;a.has(r)||a.set(r,{name:o.data.subAgentName,colorIndex:s++}),t.set(o.stableId,r)}if(a.size===0)return{ownerMap:t,subagentInfo:a};let n=new Map;for(let o of e)o.id&&n.set(o.id,o);for(let o of e)if(o.type==="assistant.message"){let r=o.data?.parentToolCallId;r&&a.has(r)&&t.set(o.stableId,r)}for(let o of e){if(o.type!=="reasoning")continue;let r=o.parentId,c=0;for(;r&&c<10;){let l=n.get(r);if(!l)break;if(l.type==="assistant.message"){let d=l.data?.parentToolCallId;d&&a.has(d)&&t.set(o.stableId,d);break}r=l.parentId,c++}}let i=new Map;for(let o of e){if(o.type!=="tool.execution_start")continue;let r=o.parentId,c=0;for(;r&&c<10;){let l=n.get(r);if(!l)break;if(l.type==="assistant.message"){let d=l.data?.parentToolCallId;if(d&&a.has(d)){t.set(o.stableId,d);let p=o.data?.toolCallId;p&&i.set(p,d)}break}r=l.parentId,c++}}for(let o of e){if(o.type!=="tool.execution_complete")continue;let r=o.data?.toolCallId;r&&i.has(r)&&t.set(o.stableId,i.get(r))}for(let o of e){if(o.type!=="tool.invocation")continue;let r=o.data?.parentToolCallId;r&&a.has(r)&&t.set(o.stableId,r)}return{ownerMap:t,subagentInfo:a}}),Ee=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),n=String(t.getSeconds()).padStart(2,"0");return`${a}:${s}:${n}`},Me=e=>{if(!e)return"";let t=new Date(e),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),n=String(t.getSeconds()).padStart(2,"0"),i=String(t.getMilliseconds()).padStart(3,"0");return`${a}:${s}:${n}.${i}`},h=new Map,ne=200,Ne=e=>{if(!e)return"";if(h.has(e))return h.get(e);try{let t=e.replace(/\\r\\n/g,`
|
|
2
2
|
`).replace(/\\n/g,`
|
|
3
|
-
`).replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\"),a={ALLOWED_TAGS:["p","br","strong","em","code","pre","a","ul","ol","li","h1","h2","h3","h4","h5","h6","blockquote","table","thead","tbody","tr","th","td","hr","del","span","div","mark"],ALLOWED_ATTR:["href","style","class"],ALLOW_DATA_ATTR:!1,ALLOWED_URI_REGEXP:/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i},s=t.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(s){let o=s[1],
|
|
4
|
-
`)
|
|
5
|
-
|
|
3
|
+
`).replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\"),a={ALLOWED_TAGS:["p","br","strong","em","code","pre","a","ul","ol","li","h1","h2","h3","h4","h5","h6","blockquote","table","thead","tbody","tr","th","td","hr","del","span","div","mark"],ALLOWED_ATTR:["href","style","class"],ALLOW_DATA_ATTR:!1,ALLOWED_URI_REGEXP:/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i},s=t.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(s){let o=s[1],r=s[2],c=o.split(`
|
|
4
|
+
`),l=[],d=0;for(;d<c.length;){let f=c[d];if(!f.trim()||!f.includes(":")){d++;continue}let z=f.indexOf(":"),H=f.substring(0,z).trim(),V=f.substring(z+1).trim();if(V==="|"||V===">"){let pe=[];for(d++;d<c.length&&(c[d].startsWith(" ")||c[d].startsWith(" ")||c[d].trim()==="");)pe.push(c[d].trim()),d++;let vt=V===">"?" ":`
|
|
5
|
+
`;l.push({key:H,value:pe.filter(pt=>pt).join(vt)})}else l.push({key:H,value:V}),d++}let p='<table style="margin-bottom: 16px; border-collapse: collapse; width: 100%;"><tbody>';l.forEach(f=>{let z=DOMPurify.sanitize(f.key,{ALLOWED_TAGS:[]}),H=DOMPurify.sanitize(f.value,{ALLOWED_TAGS:[]});p+=`<tr><td style="padding: 4px 12px; border: 1px solid #30363d; font-weight: 600; color: #7d8590;">${z}</td><td style="padding: 4px 12px; border: 1px solid #30363d;">${H}</td></tr>`}),p+="</tbody></table>";let J=marked.parse(r),R=DOMPurify.sanitize(J,a),B=p+R;if(h.size>=ne){let f=h.keys().next().value;h.delete(f)}return h.set(e,B),B}let n=marked.parse(t),i=DOMPurify.sanitize(n,a);if(h.size>=ne){let o=h.keys().next().value;h.delete(o)}return h.set(e,i),i}catch{return e}},Re=e=>{let t={...k.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,k.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"tool",action:a?"collapse":"expand",sessionId:m.value})},Le=(e,t)=>{if(!t||!t.trim()||!e)return e;let a=t.trim(),s=ce(a).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=document.createElement("div");n.innerHTML=e;let i=o=>{if(o.nodeType===Node.TEXT_NODE){let r=o.textContent,c=new RegExp(`(${s})`,"gi");if(c.test(r)){let l=r.replace(c,'<mark class="search-highlight">$1</mark>'),d=document.createElement("span");d.innerHTML=l,o.parentNode.replaceChild(d,o)}}else o.nodeType===Node.ELEMENT_NODE&&o.tagName!=="SCRIPT"&&o.tagName!=="STYLE"&&Array.from(o.childNodes).forEach(i)};return Array.from(n.childNodes).forEach(i),n.innerHTML},Ae=e=>{let t={...w.value},a=!!t[e];t[e]?delete t[e]:t[e]=!0,w.value=t,window.trackClick&&window.trackClick("EventExpanded",{eventType:"content",action:a?"collapse":"expand",sessionId:m.value})},De=e=>e?e.split(`
|
|
6
|
+
`).length>20||e.length>2e3:!1,Oe=e=>{let t=e.split(`
|
|
6
7
|
`);return t.length<=20?e:t.slice(0,20).join(`
|
|
7
8
|
`)+`
|
|
8
9
|
|
|
9
|
-
...`},Ne=(e,t)=>{if(t?.data?.badgeLabel&&t?.data?.badgeClass)return{label:t.data.badgeLabel,class:t.data.badgeClass};if(e==="message"&&t?.data?.role==="toolResult")return{label:"TOOL RESULT",class:"badge-tool"};if(e==="session.model_change")return{label:"MODEL CHANGE",class:"badge-session"};if(e==="session.truncation")return{label:"TRUNCATION",class:"badge-truncation"};if(e==="session.compaction_start"||e==="session.compaction_complete")return{label:"COMPACTION",class:"badge-compaction"};if(e==="system.notification")return{label:"SYSTEM",class:"badge-system"};let s=(e||"").split(".")[0]||"unknown";return{user:{label:"USER",class:"badge-user"},assistant:{label:"ASSISTANT",class:"badge-assistant"},reasoning:{label:"REASONING",class:"badge-reasoning"},turn:{label:"TURN",class:"badge-turn"},tool:{label:"TOOL",class:"badge-tool"},subagent:{label:"SUBAGENT",class:"badge-subagent"},skill:{label:"SKILL",class:"badge-skill"},session:{label:"SESSION",class:"badge-session"},error:{label:"ERROR",class:"badge-error"},abort:{label:"ABORT",class:"badge-error"}}[s]||{label:s.toUpperCase(),class:"badge-info"}},Le=e=>{if(!e.complete)return{icon:"\u23F3",color:"tool-status-running",text:""};let t=e.complete.data||{};return t.error||t.isError?{icon:"\u274C",color:"tool-status-error",text:""}:{icon:"\u2713",color:"tool-status-success",text:""}},Ae=e=>{if(!e.complete?.data?.error)return"";let t=e.complete.data.error;if(typeof t=="object"&&t.message)return t.message;if(typeof t=="string"){try{let a=JSON.parse(t);if(a.message)return a.message}catch{}return t}return String(t)},Re=e=>{if(!e.complete)return"";let t=new Date(e.start.timestamp).getTime(),s=new Date(e.complete.timestamp).getTime()-t;return s>=100?`${(s/1e3).toFixed(1)}s`:""},De=e=>{if(!e.start)return"";let t=e.start.data?.arguments||{},a=e.start.data?.toolName||e.tool||"",s="";if(a==="bash"||a==="exec")s=t.command||t.description||"";else if(a==="ask_user")s=t.question||t.message||"";else if(a==="read"||a==="write"||a==="edit")s=t.file_path||t.path||"";else if(a==="view")s=t.path||t.file||"";else if(a==="create")s=t.path||t.name||"";else if(a==="report_intent")s=t.intent||t.message||"";else if(a==="web_search")s=t.query||"";else if(a==="web_fetch")s=t.url||"";else if(a==="browser"){let n=t.action||"",r=t.targetUrl||t.url||"";s=r?`${n} ${r}`:n}else s=t.description||t.command||t.message||t.path||t.file_path||t.query||"";return s&&s.length>200&&(s=s.substring(0,200)+"..."),s},Oe=e=>e.data?.tools&&e.data.tools.length>0,_e=e=>e.data?.tools&&Array.isArray(e.data.tools)?e.data.tools.filter(t=>t&&typeof t=="object"&&t.name).map(t=>{let a=t.result!==void 0||t.status==="completed"||t.status==="error";return{tool:t.name,start:{data:{toolName:t.name,arguments:t.input||t.arguments||{}}},complete:a?{data:{result:t.result,error:t.status==="error"?t.error:null}}:null}}):[],ae=["#58a6ff","#f0883e","#a371f7","#3fb950","#f778ba","#79c0ff","#d29922","#56d4dd"],$e=e=>{let t=0;for(let a=0;a<e.length;a++){let s=e.charCodeAt(a);t=(t<<5)-t+s,t=t&t}return t},se=e=>{let{ownerMap:t,subagentInfo:a}=xe.value;if(e.type==="subagent.started"||e.type==="subagent.completed"||e.type==="subagent.failed"){let r=e.data?.toolCallId;if(r&&a.has(r)){let o=a.get(r);return{name:o.name,toolCallId:r,colorIndex:o.colorIndex}}return null}if(e._subagent){let r=e._subagent.id,o=e._subagent.name;if(a.has(r)){let i=a.get(r);return{name:i.name,toolCallId:r,colorIndex:i.colorIndex}}return{name:o,toolCallId:r,colorIndex:Math.abs($e(r))}}if(e.data?.subAgentId){let r=e.data.subAgentId,o=a.get(r);if(o)return{name:o.name,toolCallId:r,colorIndex:o.colorIndex}}let s=t.get(e.stableId);if(!s)return null;let n=a.get(s);return n?{name:n.name,toolCallId:s,colorIndex:n.colorIndex}:null},qe=e=>{let t=se(e);return t?ae[t.colorIndex%ae.length]:null},Ue=e=>{if(window.trackClick){let t=ee.value.find(a=>a.type===e);window.trackClick("EventFilterClicked",{filterType:e,filterLabel:t?t.label:e,sessionId:m.value})}S.value=e},oe=e=>{V.value="",S.value="all",X.value=e.id,Vue.nextTick(()=>{if(g.value){let t=A.value.findIndex(a=>a.virtualIndex===e.index);if(t>=0){let a=s=>{s<=0||!g.value||(g.value.scrollToItem(t),setTimeout(()=>a(s-1),100))};setTimeout(()=>a(3),50)}}})},Be=()=>{if(!g.value)return;let e=t=>{t<=0||!g.value||(g.value.scrollToItem(0),setTimeout(()=>e(t-1),100))};e(3)},Pe=()=>{if(!g.value)return;let e=A.value.length-1,t=a=>{a<=0||!g.value||(g.value.scrollToItem(e),setTimeout(()=>t(a-1),100))};t(5)},ne=e=>{window.trackClick&&window.trackClick("TurnClicked",{turnNumber:e,sessionId:m.value});let t=R.value.find(a=>a.id===e);if(t){let a=`UserReq${t.userReqNumber}_Turn${t.id}`,s=`${window.location.pathname}?eventType=assistant.turn_start&eventName=${a}`;window.history.pushState({},"",s),oe(t)}},ze=e=>{if(!e)return"";let t=e.replace(/\/$/,"").split("/");return t[t.length-1]||e},Ve=e=>{let t=R.value.find(s=>s.index===e);if(!t)return"?";let a=t.originalTurnId??t.id;return t.userReqNumber>0?`${t.userReqNumber} - Turn ${a}`:`Turn ${a}`},He=e=>R.value.find(a=>a.index===e)?.duration||null,re=e=>{let t=document.createElement("div");return t.textContent=e,t.innerHTML},je=e=>e?new Date(e).toLocaleString():"N/A",Fe=async()=>{console.log("[Export] exportSession called"),window.trackClick&&window.trackClick("ExportClicked",{sessionId:m.value}),z.value=!0;try{console.log("[Export] Fetching:",`/session/${m.value}/export`);let e=await fetch(`/session/${m.value}/export`);if(console.log("[Export] Response received:",e.status,e.ok),console.log("[Export] Response received:",e.status,e.ok),!e.ok)throw new Error("Share failed");console.log("[Export] Creating blob...");let t=await e.blob();console.log("[Export] Blob size:",t.size,"type:",t.type);let a=window.URL.createObjectURL(t);console.log("[Export] Creating download link...");let s=document.createElement("a");s.href=a,s.download=`session-${m.value}.zip`,document.body.appendChild(s),s.click(),console.log("[Export] Download triggered"),window.URL.revokeObjectURL(a),document.body.removeChild(s),console.log("[Export] Showing success feedback...");let n="\u{1F4E4} Share Session",r="\u2713 Downloaded!",o=document.querySelector(".export-btn");o&&(o.textContent=r,o.style.background="#238636",console.log("[Export] Button text updated to:",o.textContent),setTimeout(()=>{o.textContent=n,o.style.background="",console.log("[Export] Button text restored")},2e3))}catch(e){console.error("[Export] Share session error:",e),alert("Failed to share session: "+e.message)}finally{z.value=!1,console.log("[Export] Export complete")}};F(async()=>{try{console.log("[Navigation] Starting event loading...");let a=await fetch(`/api/sessions/${m.value}/events`);if(!a.ok)throw new Error(`Failed to load events: ${a.statusText}`);let s=await a.json();if(Array.isArray(s))I.value=s;else if(s.events&&Array.isArray(s.events))I.value=s.events,console.log("[Navigation] Pagination:",s.pagination);else throw new Error("Invalid response format");if(console.log("[Navigation] Events loaded:",I.value.length),I.value.length>0){let c=I.value[I.value.length-1],l=c.timestamp||c.time||c.data?.timestamp;l&&(W.value.updated=new Date(l))}let n=new URLSearchParams(window.location.search),r=n.get("eventType"),o=n.get("eventName"),i=n.get("eventTimestamp");console.log("[Navigation] URL params:",r,o,i),r&&o&&(console.log("[Navigation] Waiting for Vue to render..."),Vue.nextTick(()=>{console.log("[Navigation] nextTick - flatEvents count:",v.value?.length);let c=null;if(r==="assistant.turn_start"){let l=o.match(/UserReq(\d+)_Turn(\d+)/);if(l){let d=parseInt(l[2],10);if(!isNaN(d)){console.log("[Navigation] Jumping to turn:",d),ne(d);return}}}else r==="subagent.started"?(console.log("[Navigation] Searching for subagent:",o,"timestamp:",i),i&&(c=v.value.find(l=>l.type==="subagent.started"&&l.timestamp===i)),c||(c=v.value.find(l=>l.type==="subagent.started"&&(l.data?.agentDisplayName===o||l.data?.agentName===o||l.data?.label===o))),console.log("[Navigation] Target event found:",c?"YES":"NO","virtualIndex:",c?.virtualIndex)):c=v.value.find(l=>l.type===r);if(c){let l=A.value.findIndex(d=>d.virtualIndex===c.virtualIndex);if(console.log("[Navigation] Target in filteredEvents at index:",l),l>=0&&g.value){console.log("[Navigation] Scrolling to index:",l);let d=y=>{y<=0||!g.value||(g.value.scrollToItem(l),setTimeout(()=>d(y-1),100))};setTimeout(()=>d(3),50)}else console.log("[Navigation] Failed - targetIndex:",l,"scrollerRef:",!!g.value)}else console.log("[Navigation] Target event not found")}))}catch(a){console.error("Error loading events:",a),Q.value=a.message}finally{Z.value=!1}window.addEventListener("keydown",a=>{a.ctrlKey&&a.key==="b"&&(a.preventDefault(),q.value=!q.value)}),window.marked&&marked.setOptions({breaks:!0,gfm:!0});let e=()=>{if(!g.value)return;let a=null;if(g.value.$el&&typeof g.value.$el.querySelector=="function"?a=g.value.$el.querySelector(".vue-recycle-scroller"):g.value.querySelector&&typeof g.value.querySelector=="function"&&(a=g.value.querySelector(".vue-recycle-scroller")),a||(a=document.querySelector(".vue-recycle-scroller")),a){let s=a.scrollTop,n=a.clientHeight,r=80,o=Math.floor(s/r),i=Math.ceil(n/r),c=Math.min(o+i,A.value.length),l=Math.max(1,o+1),d=Math.max(1,c);Y.value={start:Math.min(l,d),end:d}}},t=null;setTimeout(()=>{e();let a=document.querySelector(".vue-recycle-scroller");a&&(a.addEventListener("scroll",e),t=()=>{a.removeEventListener("scroll",e)})},500),me(()=>{L&&(clearTimeout(L),L=null),t&&t(),x.value={},w.value={},f.clear()})});let D=u([]),H=u([]),O=u(!1),b=u([]),C=u(""),j=u(null),h=u(""),k=u(!1),B=u([]),ie=u(0),le=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"],Ge=e=>{let t=0;for(let a=0;a<e.length;a++)t=e.charCodeAt(a)+((t<<5)-t);return le[Math.abs(t)%le.length]},We=async()=>{try{let e=await fetch(`/api/sessions/${m.value}/tags`);if(e.ok){let t=await e.json();D.value=t.tags||[]}}catch(e){console.error("Error loading tags:",e)}},de=async()=>{try{let e=await fetch("/api/tags");if(e.ok){let t=await e.json();H.value=t.tags||[]}}catch(e){console.error("Error loading all tags:",e)}},Ke=async e=>{try{window.trackClick&&e.filter(s=>!D.value.includes(s)).forEach(s=>{window.trackClick("TagAdded",{sessionId:m.value,tag:s})});let t=await fetch(`/api/sessions/${m.value}/tags`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({tags:e})});if(t.ok){let a=await t.json();return D.value=a.tags||[],h.value="",!0}else{let a=await t.json();return h.value=a.error||"Failed to save tags",!1}}catch(t){return console.error("Error saving tags:",t),h.value="Network error",!1}},Je=()=>{b.value=[...D.value],O.value=!0,h.value="",setTimeout(()=>{j.value&&j.value.focus()},10)},Xe=()=>{O.value=!1,b.value=[],C.value="",k.value=!1,h.value=""},ce=()=>{let e=C.value.trim().toLowerCase();if(e){if(e.length>30){h.value="Tag must be 30 characters or less";return}if(b.value.length>=10){h.value="Maximum 10 tags per session";return}if(b.value.includes(e)){h.value="Tag already added",C.value="";return}b.value.push(e),C.value="",k.value=!1,h.value=""}},Ye=e=>{b.value=b.value.filter(t=>t!==e),h.value=""},Ze=()=>{let e=C.value.trim().toLowerCase();if(!e){k.value=!1,B.value=[];return}let t=H.value.filter(a=>a.toLowerCase().includes(e)&&!b.value.includes(a)).slice(0,5);t.length>0?(k.value=!0,B.value=t,ie.value=0):(k.value=!1,B.value=[])},Qe=e=>{C.value=e,ce()},et=async()=>{setTimeout(async()=>{if(!O.value)return;await Ke(b.value)&&(O.value=!1,b.value=[],C.value="",k.value=!1,await de())},200)};return F(async()=>{await We(),await de()}),{sessionId:m,metadata:W,exporting:z,sidebarCollapsed:q,expandedTools:x,expandedContent:w,expansionCount:he,currentFilter:S,searchText:V,currentTurnIndex:X,scrollerRef:g,visibleRange:Y,loadedEvents:I,eventsLoading:Z,eventsError:Q,flatEvents:v,filteredEvents:A,eventCounts:fe,filters:ee,turns:R,userReqs:Te,truncateText:ye,formatTime:we,formatDateTime:je,renderMarkdown:Ie,highlightSearchText:ke,toggleTool:Ce,toggleContent:Se,isContentTooLong:Ee,truncateContent:Me,getBadgeInfo:Ne,getToolStatus:Le,getToolErrorMessage:Ae,getToolDuration:Re,getToolCommand:De,hasTools:Oe,getToolGroups:_e,getSubagentInfo:se,getSubagentColor:qe,setFilter:Ue,scrollToTurn:oe,scrollToTop:Be,scrollToBottom:Pe,jumpToTurn:ne,getTurnNumber:Ve,getTurnDuration:He,repoBasename:ze,escapeHtml:re,exportSession:Fe,searchResultCount:be,sessionTags:D,allTags:H,tagsEditing:O,editingTags:b,tagInputValue:C,tagInputRef:j,tagsError:h,showAutocomplete:k,autocompleteOptions:B,autocompleteSelectedIndex:ie,getTagColor:Ge,startEditTags:Je,cancelEditTags:Xe,addTag:ce,removeTagFromEdit:Ye,updateAutocomplete:Ze,selectAutocompleteOption:Qe,saveTagsOnBlur:et}},template:`
|
|
10
|
+
...`},_e=(e,t)=>{if(t?.data?.badgeLabel&&t?.data?.badgeClass)return{label:t.data.badgeLabel,class:t.data.badgeClass};if(e==="message"&&t?.data?.role==="toolResult")return{label:"TOOL RESULT",class:"badge-tool"};if(e==="session.model_change")return{label:"MODEL CHANGE",class:"badge-session"};if(e==="session.truncation")return{label:"TRUNCATION",class:"badge-truncation"};if(e==="session.compaction_start"||e==="session.compaction_complete")return{label:"COMPACTION",class:"badge-compaction"};if(e==="system.notification")return{label:"SYSTEM",class:"badge-system"};let s=(e||"").split(".")[0]||"unknown";return{user:{label:"USER",class:"badge-user"},assistant:{label:"ASSISTANT",class:"badge-assistant"},reasoning:{label:"REASONING",class:"badge-reasoning"},turn:{label:"TURN",class:"badge-turn"},tool:{label:"TOOL",class:"badge-tool"},subagent:{label:"SUBAGENT",class:"badge-subagent"},skill:{label:"SKILL",class:"badge-skill"},session:{label:"SESSION",class:"badge-session"},error:{label:"ERROR",class:"badge-error"},abort:{label:"ABORT",class:"badge-error"}}[s]||{label:s.toUpperCase(),class:"badge-info"}},$e=e=>{if(!e.complete)return{icon:"\u23F3",color:"tool-status-running",text:""};let t=e.complete.data||{};return t.error||t.isError?{icon:"\u274C",color:"tool-status-error",text:""}:{icon:"\u2713",color:"tool-status-success",text:""}},qe=e=>{if(!e.complete?.data?.error)return"";let t=e.complete.data.error;if(typeof t=="object"&&t.message)return t.message;if(typeof t=="string"){try{let a=JSON.parse(t);if(a.message)return a.message}catch{}return t}return String(t)},Ue=e=>{if(!e.complete)return"";let t=new Date(e.start.timestamp).getTime(),s=new Date(e.complete.timestamp).getTime()-t;return s>=100?`${parseFloat((s/1e3).toPrecision(3))}s`:""},ft=e=>{let t={};if(e.start?.timestamp&&(t.startTime=e.start.timestamp),e.complete?.timestamp&&(t.endTime=e.complete.timestamp),t.startTime&&t.endTime){let a=new Date(t.endTime).getTime()-new Date(t.startTime).getTime();a>=0&&(t.duration=`${parseFloat((a/1e3).toPrecision(3))}s (${a}ms)`)}return t},Pe=e=>{if(!e.start)return"";let t=e.start.data?.arguments||{},a=e.start.data?.toolName||e.tool||"",s="";if(a==="bash"||a==="exec")s=t.command||t.description||"";else if(a==="ask_user")s=t.question||t.message||"";else if(a==="read"||a==="write"||a==="edit")s=t.file_path||t.path||"";else if(a==="view")s=t.path||t.file||"";else if(a==="create")s=t.path||t.name||"";else if(a==="report_intent")s=t.intent||t.message||"";else if(a==="web_search")s=t.query||"";else if(a==="web_fetch")s=t.url||"";else if(a==="browser"){let n=t.action||"",i=t.targetUrl||t.url||"";s=i?`${n} ${i}`:n}else s=t.description||t.command||t.message||t.path||t.file_path||t.query||"";return s&&s.length>200&&(s=s.substring(0,200)+"..."),s},Be=e=>e.data?.tools&&e.data.tools.length>0,ze=e=>e.data?.tools&&Array.isArray(e.data.tools)?e.data.tools.filter(t=>t&&typeof t=="object"&&t.name).map(t=>{let a=t.result!==void 0||t.status==="completed"||t.status==="error",s={};if(t.startTime&&(s.startTime=t.startTime),t.endTime&&(s.endTime=t.endTime),s.startTime&&s.endTime){let n=new Date(s.endTime).getTime()-new Date(s.startTime).getTime();n>=0&&(s.duration=`${parseFloat((n/1e3).toPrecision(3))}s (${n}ms)`)}return{tool:t.name,timing:s,start:{timestamp:t.startTime,data:{toolName:t.name,arguments:t.input||t.arguments||{}}},complete:a?{timestamp:t.endTime,data:{result:t.result,error:t.status==="error"?t.error:null}}:null}}):[],ie=["#58a6ff","#f0883e","#a371f7","#3fb950","#f778ba","#79c0ff","#d29922","#56d4dd"],He=e=>{let t=0;for(let a=0;a<e.length;a++){let s=e.charCodeAt(a);t=(t<<5)-t+s,t=t&t}return t},re=e=>{let{ownerMap:t,subagentInfo:a}=Ie.value;if(e.type==="subagent.started"||e.type==="subagent.completed"||e.type==="subagent.failed"){let i=e.data?.toolCallId;if(i&&a.has(i)){let o=a.get(i);return{name:o.name,toolCallId:i,colorIndex:o.colorIndex}}return null}if(e._subagent){let i=e._subagent.id,o=e._subagent.name;if(a.has(i)){let r=a.get(i);return{name:r.name,toolCallId:i,colorIndex:r.colorIndex}}return{name:o,toolCallId:i,colorIndex:Math.abs(He(i))}}if(e.data?.subAgentId){let i=e.data.subAgentId,o=a.get(i);if(o)return{name:o.name,toolCallId:i,colorIndex:o.colorIndex}}let s=t.get(e.stableId);if(!s)return null;let n=a.get(s);return n?{name:n.name,toolCallId:s,colorIndex:n.colorIndex}:null},Ve=e=>{let t=re(e);return t?ie[t.colorIndex%ie.length]:null},je=e=>{if(window.trackClick){let t=oe.value.find(a=>a.type===e);window.trackClick("EventFilterClicked",{filterType:e,filterLabel:t?t.label:e,sessionId:m.value})}E.value=e},le=e=>{F.value="",E.value="all",ee.value=e.id,Vue.nextTick(()=>{if(g.value){let t=A.value.findIndex(a=>a.virtualIndex===e.index);if(t>=0){let a=s=>{s<=0||!g.value||(g.value.scrollToItem(t),setTimeout(()=>a(s-1),100))};setTimeout(()=>a(3),50)}}})},Fe=()=>{if(!g.value)return;let e=t=>{t<=0||!g.value||(g.value.scrollToItem(0),setTimeout(()=>e(t-1),100))};e(3)},We=()=>{if(!g.value)return;let e=A.value.length-1,t=a=>{a<=0||!g.value||(g.value.scrollToItem(e),setTimeout(()=>t(a-1),100))};t(5)},de=e=>{window.trackClick&&window.trackClick("TurnClicked",{turnNumber:e,sessionId:m.value});let t=D.value.find(a=>a.id===e);if(t){let a=`UserReq${t.userReqNumber}_Turn${t.id}`,s=`${window.location.pathname}?eventType=assistant.turn_start&eventName=${a}`;window.history.pushState({},"",s),le(t)}},Ge=e=>{if(!e)return"";let t=e.replace(/\/$/,"").split("/");return t[t.length-1]||e},Ke=e=>{let t=D.value.find(s=>s.index===e);if(!t)return"?";let a=t.originalTurnId??t.id;return t.userReqNumber>0?`${t.userReqNumber} - Turn ${a}`:`Turn ${a}`},Je=e=>D.value.find(a=>a.index===e)?.duration||null,ce=e=>{let t=document.createElement("div");return t.textContent=e,t.innerHTML},Xe=e=>e?new Date(e).toLocaleString():"N/A",Ye=async()=>{console.log("[Export] exportSession called"),window.trackClick&&window.trackClick("ExportClicked",{sessionId:m.value}),j.value=!0;try{console.log("[Export] Fetching:",`/session/${m.value}/export`);let e=await fetch(`/session/${m.value}/export`);if(console.log("[Export] Response received:",e.status,e.ok),console.log("[Export] Response received:",e.status,e.ok),!e.ok)throw new Error("Share failed");console.log("[Export] Creating blob...");let t=await e.blob();console.log("[Export] Blob size:",t.size,"type:",t.type);let a=window.URL.createObjectURL(t);console.log("[Export] Creating download link...");let s=document.createElement("a");s.href=a,s.download=`session-${m.value}.zip`,document.body.appendChild(s),s.click(),console.log("[Export] Download triggered"),window.URL.revokeObjectURL(a),document.body.removeChild(s),console.log("[Export] Showing success feedback...");let n="\u{1F4E4} Share Session",i="\u2713 Downloaded!",o=document.querySelector(".export-btn");o&&(o.textContent=i,o.style.background="#238636",console.log("[Export] Button text updated to:",o.textContent),setTimeout(()=>{o.textContent=n,o.style.background="",console.log("[Export] Button text restored")},2e3))}catch(e){console.error("[Export] Share session error:",e),alert("Failed to share session: "+e.message)}finally{j.value=!1,console.log("[Export] Export complete")}};X(async()=>{try{console.log("[Navigation] Starting event loading...");let a=await fetch(`/api/sessions/${m.value}/events`);if(!a.ok)throw new Error(`Failed to load events: ${a.statusText}`);let s=await a.json();if(Array.isArray(s))C.value=s;else if(s.events&&Array.isArray(s.events))C.value=s.events,console.log("[Navigation] Pagination:",s.pagination);else throw new Error("Invalid response format");if(console.log("[Navigation] Events loaded:",C.value.length),C.value.length>0){let c=C.value[C.value.length-1],l=c.timestamp||c.time||c.data?.timestamp;l&&(b.value.updated=new Date(l))}let n=new URLSearchParams(window.location.search),i=n.get("eventType"),o=n.get("eventName"),r=n.get("eventTimestamp");console.log("[Navigation] URL params:",i,o,r),i&&o&&(console.log("[Navigation] Waiting for Vue to render..."),Vue.nextTick(()=>{console.log("[Navigation] nextTick - flatEvents count:",T.value?.length);let c=null;if(i==="assistant.turn_start"){let l=o.match(/UserReq(\d+)_Turn(\d+)/);if(l){let d=parseInt(l[2],10);if(!isNaN(d)){console.log("[Navigation] Jumping to turn:",d),de(d);return}}}else i==="subagent.started"?(console.log("[Navigation] Searching for subagent:",o,"timestamp:",r),r&&(c=T.value.find(l=>l.type==="subagent.started"&&l.timestamp===r)),c||(c=T.value.find(l=>l.type==="subagent.started"&&(l.data?.agentDisplayName===o||l.data?.agentName===o||l.data?.label===o))),console.log("[Navigation] Target event found:",c?"YES":"NO","virtualIndex:",c?.virtualIndex)):c=T.value.find(l=>l.type===i);if(c){let l=A.value.findIndex(d=>d.virtualIndex===c.virtualIndex);if(console.log("[Navigation] Target in filteredEvents at index:",l),l>=0&&g.value){console.log("[Navigation] Scrolling to index:",l);let d=p=>{p<=0||!g.value||(g.value.scrollToItem(l),setTimeout(()=>d(p-1),100))};setTimeout(()=>d(3),50)}else console.log("[Navigation] Failed - targetIndex:",l,"scrollerRef:",!!g.value)}else console.log("[Navigation] Target event not found")}))}catch(a){console.error("Error loading events:",a),se.value=a.message}finally{ae.value=!1}window.addEventListener("keydown",a=>{a.ctrlKey&&a.key==="b"&&(a.preventDefault(),q.value=!q.value)}),window.marked&&marked.setOptions({breaks:!0,gfm:!0});let e=()=>{if(!g.value)return;let a=null;if(g.value.$el&&typeof g.value.$el.querySelector=="function"?a=g.value.$el.querySelector(".vue-recycle-scroller"):g.value.querySelector&&typeof g.value.querySelector=="function"&&(a=g.value.querySelector(".vue-recycle-scroller")),a||(a=document.querySelector(".vue-recycle-scroller")),a){let s=a.scrollTop,n=a.clientHeight,i=80,o=Math.floor(s/i),r=Math.ceil(n/i),c=Math.min(o+r,A.value.length),l=Math.max(1,o+1),d=Math.max(1,c);te.value={start:Math.min(l,d),end:d}}},t=null;setTimeout(()=>{e();let a=document.querySelector(".vue-recycle-scroller");a&&(a.addEventListener("scroll",e),t=()=>{a.removeEventListener("scroll",e)})},500),be(()=>{L&&(clearTimeout(L),L=null),t&&t(),k.value={},w.value={},h.clear()})});let O=u([]),W=u([]),_=u(!1),y=u([]),S=u(""),G=u(null),x=u(""),I=u(!1),P=u([]),ue=u(0),K=u(!1),Ze=e=>!e||e===0?"0":e<1e3?e.toString():Math.floor(e/1e3)+"K",Qe=e=>{if(!e||e===0)return"0s";let t=Math.floor(e/1e3);if(t<60)return(e/1e3).toFixed(1)+"s";let a=Math.floor(t/60),s=t%60;return`${a}m ${s}s`},et=v(()=>{if(!b.value.usage||!b.value.usage.modelMetrics)return 0;let e=0;for(let t in b.value.usage.modelMetrics){let a=b.value.usage.modelMetrics[t].usage;a&&(e+=(a.inputTokens||0)+(a.outputTokens||0))}return e}),tt=v(()=>{if(!b.value.usage||!b.value.usage.modelMetrics)return 0;let e=0;for(let t in b.value.usage.modelMetrics)e+=b.value.usage.modelMetrics[t].requests?.count||0;return e}),at=e=>{let t=b.value.usage?.modelMetrics[e];if(!t||!t.usage)return null;let a=t.usage.cacheReadTokens||0,s=t.usage.inputTokens||0;return s===0?null:Math.round(a/s*100)},st=e=>e==null?"":e+" premium",ot=()=>{K.value=!K.value},me=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#ec4899","#06b6d4","#f97316"],nt=e=>{let t=0;for(let a=0;a<e.length;a++)t=e.charCodeAt(a)+((t<<5)-t);return me[Math.abs(t)%me.length]},it=async()=>{try{let e=await fetch(`/api/sessions/${m.value}/tags`);if(e.ok){let t=await e.json();O.value=t.tags||[]}}catch(e){console.error("Error loading tags:",e)}},ge=async()=>{try{let e=await fetch("/api/tags");if(e.ok){let t=await e.json();W.value=t.tags||[]}}catch(e){console.error("Error loading all tags:",e)}},rt=async e=>{try{window.trackClick&&e.filter(s=>!O.value.includes(s)).forEach(s=>{window.trackClick("TagAdded",{sessionId:m.value,tag:s})});let t=await fetch(`/api/sessions/${m.value}/tags`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({tags:e})});if(t.ok){let a=await t.json();return O.value=a.tags||[],x.value="",!0}else{let a=await t.json();return x.value=a.error||"Failed to save tags",!1}}catch(t){return console.error("Error saving tags:",t),x.value="Network error",!1}},lt=()=>{y.value=[...O.value],_.value=!0,x.value="",setTimeout(()=>{G.value&&G.value.focus()},10)},dt=()=>{_.value=!1,y.value=[],S.value="",I.value=!1,x.value=""},ve=()=>{let e=S.value.trim().toLowerCase();if(e){if(e.length>30){x.value="Tag must be 30 characters or less";return}if(y.value.length>=10){x.value="Maximum 10 tags per session";return}if(y.value.includes(e)){x.value="Tag already added",S.value="";return}y.value.push(e),S.value="",I.value=!1,x.value=""}},ct=e=>{y.value=y.value.filter(t=>t!==e),x.value=""},ut=()=>{let e=S.value.trim().toLowerCase();if(!e){I.value=!1,P.value=[];return}let t=W.value.filter(a=>a.toLowerCase().includes(e)&&!y.value.includes(a)).slice(0,5);t.length>0?(I.value=!0,P.value=t,ue.value=0):(I.value=!1,P.value=[])},mt=e=>{S.value=e,ve()},gt=async()=>{setTimeout(async()=>{if(!_.value)return;await rt(y.value)&&(_.value=!1,y.value=[],S.value="",I.value=!1,await ge())},200)};return X(async()=>{await it(),await ge()}),{sessionId:m,metadata:b,exporting:j,sidebarCollapsed:q,expandedTools:k,expandedContent:w,expansionCount:we,currentFilter:E,searchText:F,currentTurnIndex:ee,scrollerRef:g,visibleRange:te,loadedEvents:C,eventsLoading:ae,eventsError:se,flatEvents:T,filteredEvents:A,eventCounts:xe,filters:oe,turns:D,userReqs:Ce,truncateText:Se,formatTime:Ee,formatToolTime:Me,formatDateTime:Xe,renderMarkdown:Ne,highlightSearchText:Le,toggleTool:Re,toggleContent:Ae,isContentTooLong:De,truncateContent:Oe,getBadgeInfo:_e,getToolStatus:$e,getToolErrorMessage:qe,getToolDuration:Ue,getToolCommand:Pe,hasTools:Be,getToolGroups:ze,getSubagentInfo:re,getSubagentColor:Ve,setFilter:je,scrollToTurn:le,scrollToTop:Fe,scrollToBottom:We,jumpToTurn:de,getTurnNumber:Ke,getTurnDuration:Je,repoBasename:Ge,escapeHtml:ce,exportSession:Ye,searchResultCount:ke,sessionTags:O,allTags:W,tagsEditing:_,editingTags:y,tagInputValue:S,tagInputRef:G,tagsError:x,showAutocomplete:I,autocompleteOptions:P,autocompleteSelectedIndex:ue,getTagColor:nt,startEditTags:lt,cancelEditTags:dt,addTag:ve,removeTagFromEdit:ct,updateAutocomplete:ut,selectAutocompleteOption:mt,saveTagsOnBlur:gt,usageExpanded:K,toggleUsage:ot,formatTokens:Ze,formatDuration:Qe,formatCost:st,totalTokens:et,totalRequests:tt,getCacheHitRatio:at}},template:`
|
|
10
11
|
<div class="container">
|
|
11
12
|
<div class="header">
|
|
12
13
|
<a href="/" class="home-btn">\u2190 Back to Home</a>
|
|
@@ -77,6 +78,73 @@
|
|
|
77
78
|
</div>
|
|
78
79
|
</div>
|
|
79
80
|
|
|
81
|
+
<!-- Usage Section -->
|
|
82
|
+
<div v-if="metadata.usage" class="sidebar-section">
|
|
83
|
+
<div class="sidebar-section-title">Usage</div>
|
|
84
|
+
<div class="usage-container">
|
|
85
|
+
<!-- Compact view (always visible) -->
|
|
86
|
+
<div class="usage-compact" @click="toggleUsage">
|
|
87
|
+
{{ totalRequests }} reqs \xB7 {{ formatTokens(totalTokens) }} tokens \xB7 {{ formatDuration(metadata.usage.totalApiDurationMs) }}
|
|
88
|
+
<span class="usage-expand-icon">{{ usageExpanded ? '\u25B2' : '\u25BC' }}</span>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- Expanded view -->
|
|
92
|
+
<div v-if="usageExpanded" class="usage-expanded">
|
|
93
|
+
<!-- Model breakdown -->
|
|
94
|
+
<div v-if="Object.keys(metadata.usage.modelMetrics).length > 0" class="usage-section">
|
|
95
|
+
<div class="usage-section-title">Models</div>
|
|
96
|
+
<div v-for="(metrics, model) in metadata.usage.modelMetrics" :key="model" class="usage-model">
|
|
97
|
+
<div class="usage-model-name">{{ model }}</div>
|
|
98
|
+
<div class="usage-model-details">
|
|
99
|
+
<div>{{ metrics.requests?.count || 0 }} reqs</div>
|
|
100
|
+
<div v-if="metrics.requests?.cost">Cost: {{ formatCost(metrics.requests.cost) }}</div>
|
|
101
|
+
<div v-if="metrics.usage">
|
|
102
|
+
<span class="usage-token-label">In:</span> {{ formatTokens(metrics.usage.inputTokens || 0) }}
|
|
103
|
+
<span class="usage-token-label">Out:</span> {{ formatTokens(metrics.usage.outputTokens || 0) }}
|
|
104
|
+
</div>
|
|
105
|
+
<div v-if="metrics.usage?.cacheReadTokens">
|
|
106
|
+
<span class="usage-token-label">Cache Read:</span> {{ formatTokens(metrics.usage.cacheReadTokens) }}
|
|
107
|
+
<span v-if="getCacheHitRatio(model)" class="usage-cache-ratio">({{ getCacheHitRatio(model) }}%)</span>
|
|
108
|
+
</div>
|
|
109
|
+
<div v-if="metrics.usage?.cacheWriteTokens">
|
|
110
|
+
<span class="usage-token-label">Cache Write:</span> {{ formatTokens(metrics.usage.cacheWriteTokens) }}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<!-- Context window breakdown -->
|
|
117
|
+
<div v-if="metadata.usage.currentTokens || metadata.usage.systemTokens || metadata.usage.conversationTokens || metadata.usage.toolDefinitionsTokens" class="usage-section">
|
|
118
|
+
<div class="usage-section-title">Context Window</div>
|
|
119
|
+
<div class="usage-context">
|
|
120
|
+
<div v-if="metadata.usage.currentTokens">
|
|
121
|
+
<span class="usage-token-label">Current:</span> {{ formatTokens(metadata.usage.currentTokens) }}
|
|
122
|
+
</div>
|
|
123
|
+
<div v-if="metadata.usage.systemTokens">
|
|
124
|
+
<span class="usage-token-label">System:</span> {{ formatTokens(metadata.usage.systemTokens) }}
|
|
125
|
+
</div>
|
|
126
|
+
<div v-if="metadata.usage.conversationTokens">
|
|
127
|
+
<span class="usage-token-label">Conversation:</span> {{ formatTokens(metadata.usage.conversationTokens) }}
|
|
128
|
+
</div>
|
|
129
|
+
<div v-if="metadata.usage.toolDefinitionsTokens">
|
|
130
|
+
<span class="usage-token-label">Tools:</span> {{ formatTokens(metadata.usage.toolDefinitionsTokens) }}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- Code changes -->
|
|
136
|
+
<div v-if="metadata.usage.codeChanges && (metadata.usage.codeChanges.linesAdded > 0 || metadata.usage.codeChanges.linesRemoved > 0)" class="usage-section">
|
|
137
|
+
<div class="usage-section-title">Code Changes</div>
|
|
138
|
+
<div class="usage-code-changes">
|
|
139
|
+
<span class="usage-code-added">+{{ metadata.usage.codeChanges.linesAdded }}</span>
|
|
140
|
+
<span class="usage-code-removed">-{{ metadata.usage.codeChanges.linesRemoved }}</span>
|
|
141
|
+
<span class="usage-code-files">({{ metadata.usage.codeChanges.filesModified?.length || 0 }} files)</span>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
80
148
|
<div class="sidebar-section">
|
|
81
149
|
<div class="sidebar-section-title">Event Filters</div>
|
|
82
150
|
<div class="event-filters">
|
|
@@ -211,6 +279,7 @@
|
|
|
211
279
|
ref="scrollerRef"
|
|
212
280
|
:items="filteredEvents"
|
|
213
281
|
:min-item-size="80"
|
|
282
|
+
:prerender="10"
|
|
214
283
|
key-field="stableId"
|
|
215
284
|
class="scroller"
|
|
216
285
|
>
|
|
@@ -393,10 +462,32 @@
|
|
|
393
462
|
</div>
|
|
394
463
|
|
|
395
464
|
<!-- No content at all (no message and no tools) -->
|
|
396
|
-
<div v-else-if="!hasTools(item)" class="event-content" style="color: #7d8590; font-style: italic;">
|
|
465
|
+
<div v-else-if="!hasTools(item) && !item.data?.reasoningText" class="event-content" style="color: #7d8590; font-style: italic;">
|
|
397
466
|
No available message
|
|
398
467
|
</div>
|
|
399
468
|
|
|
469
|
+
<!-- Reasoning text (shown after main content, before tool calls) -->
|
|
470
|
+
<div v-if="item.data?.reasoningText" class="event-content reasoning-text-content">
|
|
471
|
+
<div
|
|
472
|
+
v-html="highlightSearchText(
|
|
473
|
+
renderMarkdown(
|
|
474
|
+
(expandedContent[item.stableId + '-reasoning'] || !isContentTooLong(item.data.reasoningText))
|
|
475
|
+
? item.data.reasoningText
|
|
476
|
+
: truncateContent(item.data.reasoningText)
|
|
477
|
+
),
|
|
478
|
+
searchText
|
|
479
|
+
)"
|
|
480
|
+
></div>
|
|
481
|
+
<div v-if="isContentTooLong(item.data.reasoningText)" style="margin-top: 8px;">
|
|
482
|
+
<button
|
|
483
|
+
@click="toggleContent(item.stableId + '-reasoning')"
|
|
484
|
+
style="background: none; border: 1px solid #30363d; color: #58a6ff; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 13px;"
|
|
485
|
+
>
|
|
486
|
+
{{ expandedContent[item.stableId + '-reasoning'] ? 'Show less \u25B2' : 'Show more \u25BC' }}
|
|
487
|
+
</button>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
400
491
|
<!-- Tool calls section (independent of message content, but don't need "No available message" if tools exist) -->
|
|
401
492
|
<div v-if="hasTools(item)" class="tool-list">
|
|
402
493
|
<div
|
|
@@ -417,6 +508,13 @@
|
|
|
417
508
|
</div>
|
|
418
509
|
|
|
419
510
|
<div v-if="expandedTools[item.stableId + '-' + idx]" class="tool-detail">
|
|
511
|
+
<div v-if="group.timing.startTime || group.timing.endTime || group.timing.duration" class="tool-detail-section">
|
|
512
|
+
<div class="tool-detail-content tool-timing-line">
|
|
513
|
+
<span v-if="group.timing.startTime"><span class="tool-timing-label">Start</span> {{ formatToolTime(group.timing.startTime) }}</span>
|
|
514
|
+
<span v-if="group.timing.endTime"><span class="tool-timing-label">Complete</span> {{ formatToolTime(group.timing.endTime) }}</span>
|
|
515
|
+
<span v-if="group.timing.duration"><span class="tool-timing-label">Duration</span> {{ group.timing.duration }}</span>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
420
518
|
<div v-if="group.start?.data?.arguments" class="tool-detail-section">
|
|
421
519
|
<div class="tool-detail-title">Arguments:</div>
|
|
422
520
|
<div class="tool-detail-content">
|
|
@@ -458,4 +556,4 @@
|
|
|
458
556
|
</div>
|
|
459
557
|
|
|
460
558
|
</div>
|
|
461
|
-
`});console.log("Mounting Vue app to #app..."),console.log("App config:",
|
|
559
|
+
`});console.log("Mounting Vue app to #app..."),console.log("App config:",Y.config),console.log("Target element:",document.getElementById("app"));try{let m=Y.mount("#app");console.log("Vue app mounted successfully!",m?"Instance created":"No instance"),console.log("VM type:",typeof m,"Has exportSession:",typeof m?.exportSession),console.log("VM keys:",m?Object.keys(m).slice(0,10):"NO_VM"),console.log("#app innerHTML length:",document.getElementById("app").innerHTML.length),console.log("#app first 100 chars:",document.getElementById("app").innerHTML.substring(0,100))}catch(m){console.error("Mount failed:",m),console.error("Error stack:",m.stack)}})();})();
|