sessiongraph-app-darwin-arm64 1.0.10 → 1.0.11
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/package.json +2 -2
- package/payload/SessionGraph.app/Contents/Info.plist +2 -2
- package/payload/SessionGraph.app/Contents/MacOS/sessiongraph +0 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/node +2 -2
- package/payload/SessionGraph.app/Contents/Resources/resources/server.mjs +6 -5
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/external-links-CJk2OPKW.js +1 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/index-DOYyn-b_.js +1 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-BtLEIWKh.js → index-DPovJIlT.js} +12 -12
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-RfmyzBnX.js → index-DhlS3ZId.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-BNX-8Jnn.js → index-JjTdnq9-.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/index-cVgPCrFV.css +1 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{mp4-worker-HFPra5JP.js → mp4-worker-Dc_1QUwS.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/publishing-CkVIjq_f.js +25 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{scene-B_CpQ0qP.js → scene-BXs8B_Kk.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/share-jfOenMTA.js +7 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/index.html +2 -2
- package/payload/SessionGraph.app/Contents/_CodeSignature/CodeResources +53 -53
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/gif-worker-CghHEBpp.js +0 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/index-BcoCDV6F.js +0 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/index-BnhC5gO7.css +0 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/publishing-DoHRK4Ea.js +0 -26
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/share-DPdu1Hbo.js +0 -8
package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/publishing-DoHRK4Ea.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import{h as ue,m as pe,D as me,a as ne,j as F,k as ae,t as ge,P as be,g as he}from"./index-BtLEIWKh.js";import{TimelineScene as fe}from"./scene-B_CpQ0qP.js";import"./three-D_1Qt16u.js";const ye={tokens:"Token counts",timestamps:"Session timestamps",messageCounts:"Message counts",messageTimestamps:"Message timestamps",model:"Model used",harness:"Harness used"},z={tokens:!0,timestamps:!0,messageCounts:!0,messageTimestamps:!0,model:!0,harness:!0};function ie(e){const r={...z};if(le(e))for(const i of Object.keys(r))typeof e[i]=="boolean"&&(r[i]=e[i]);return(!r.timestamps||!r.messageCounts)&&(r.messageTimestamps=!1),r}const oe=1e4,ve=2e6,we=1e5,ke="Always excluded: project and session names, messages, code, paths, source IDs, and costs. Anonymous groups and recorded branch links are included. Your GitHub username identifies this profile. Your published graph will appear in Explore graphs. Selected timestamps and usage will be public.",Se=/^(?:gpt-[3-6](?:\.[0-9])?(?:-(?:turbo|mini|nano|pro|codex|astra|instant|thinking|chat|latest|preview|[0-9]{4,8}))*|o[1-4](?:-(?:mini|pro|preview|[0-9]{4,8}))*|claude-(?:(?:sonnet|opus|haiku)-[3-5](?:[.-][0-9])?|[3-5](?:-[0-9])?-(?:sonnet|opus|haiku))(?:-(?:latest|thinking|[0-9]{8}))*|gemini-[1-4](?:\.[0-9])?-(?:pro|flash)(?:-(?:lite|preview|exp|latest|[0-9]{2,4}))*|deepseek-(?:chat|reasoner|v[3-4](?:\.[0-9])?|r1)|grok-[2-5](?:\.[0-9])?(?:-(?:fast|code|reasoning|non-reasoning|[0-9]))*|kimi-k[1-3](?:\.[0-9])?(?:-thinking)?|minimax-m[1-3](?:\.[0-9])?)$/i,Ce=e=>{const r=e.split("/").at(-1)||"";return r.length<=100&&Se.test(r)?r.toLowerCase():null},le=e=>!!e&&typeof e=="object"&&!Array.isArray(e),G=(e,r)=>le(e)&&Object.keys(e).length===r.length&&r.every(i=>Object.hasOwn(e,i)),B=(e,r=Number.MAX_SAFE_INTEGER)=>Number.isSafeInteger(e)&&e>=0&&e<=r,J=e=>B(e,Date.UTC(2100,0,1)),X=e=>e===null||B(e);function ce(e){const r=()=>{throw new Error("Invalid anonymous graph. Reopen the publishing preview and try again.")};if(!G(e,["version","settings","sessions"])||e.version!==1||!Array.isArray(e.sessions)||!G(e.settings,Object.keys(z))||Object.values(e.settings).some(g=>typeof g!="boolean"))return r();const i=e.settings;if(i.messageTimestamps&&(!i.timestamps||!i.messageCounts))return r();if(e.sessions.length<1||e.sessions.length>oe)throw new Error(`Choose between 1 and ${oe.toLocaleString("en")} sessions to publish.`);const t=["id","group","parent","fork",...i.timestamps?["timestamps"]:[],...i.tokens?["tokens"]:[],...i.messageCounts?["messages"]:[],...i.messageTimestamps?["messageTimes"]:[],...i.model?["model"]:[],...i.harness?["harness"]:[]];let u=0;for(let g=0;g<e.sessions.length;g++){const a=e.sessions[g];if(!G(a,t)||a.id!==g||!B(a.group,e.sessions.length-1))return r();for(const b of["parent","fork"])if(a[b]!==null&&(!B(a[b],e.sessions.length-1)||a[b]===g))return r();if(i.timestamps&&(!G(a.timestamps,["created","lastMessageAt"])||!J(a.timestamps.created)||a.timestamps.lastMessageAt!==null&&!J(a.timestamps.lastMessageAt))||i.tokens&&!X(a.tokens)||i.messageCounts&&(!G(a.messages,["count","untimed","partial"])||!X(a.messages.count)||!X(a.messages.untimed)||typeof a.messages.partial!="boolean")||i.messageTimestamps&&(!G(a.messageTimes,["values","limited"])||!Array.isArray(a.messageTimes.values)||a.messageTimes.values.length>2e3||typeof a.messageTimes.limited!="boolean"||a.messageTimes.values.some(b=>!J(b))||(u+=a.messageTimes.values.length,u>we))||i.model&&a.model!==null&&(typeof a.model!="string"||Ce(a.model)!==a.model)||i.harness&&a.harness!==null&&(typeof a.harness!="string"||!ue(a.harness)))return r()}const n=e.sessions,l=new Uint8Array(n.length);for(let g=0;g<n.length;g++){if(l[g])continue;const a=[[g,!1]];for(;a.length;){const[b,E]=a.pop();if(E){l[b]=2;continue}if(l[b]===1)return r();if(l[b]!==2){l[b]=1,a.push([b,!0]);for(const v of[n[b].parent,n[b].fork])v!==null&&a.push([v,!1])}}}if(new TextEncoder().encode(JSON.stringify(e)).length>ve)throw new Error("This graph is too large. Choose a smaller date range.");return e}function re(e,r){const i=ce(e),t=i.sessions.filter(l=>!r||r.has(l.id)).map(l=>({id:String(l.id),projectID:String(l.group),title:`Session ${l.id+1}`,directory:"",created:l.timestamps?.created??l.id*36e5,updated:l.timestamps?.created??l.id*36e5,tokens:l.tokens??null,cost:null,archived:!1,model:l.model??"",provider:"",agent:"",source:l.harness??void 0,activity:{firstMessageAt:null,lastMessageAt:l.timestamps?.lastMessageAt??null,userMessages:0,untimedUserMessages:0},...l.parent===null?{}:{parentID:String(l.parent)},...l.fork===null?{}:{forkID:String(l.fork)}})),u=[...new Set(i.sessions.map(l=>l.group))].map(l=>({id:String(l),name:`Group ${l+1}`,directory:"",worktrees:[]})),n=pe(t.map(l=>({...l,source:void 0})),u);for(const l of n.nodes)l.session.source=i.sessions[Number(l.session.id)].harness??void 0;return n}function xe(e){const r=e.settings.tokens?e.sessions.filter(t=>typeof t.tokens=="number"):[],i=r.reduce((t,u)=>t+u.tokens,0);return{sessionCount:e.sessions.length,tokenCount:r.length&&Number.isSafeInteger(i)?i:null,tokensPartial:r.length>0&&r.length<e.sessions.length}}function de(e){const r=xe(e),i=[`${r.sessionCount.toLocaleString()} sessions`];if(e.settings.tokens&&i.push(r.tokenCount===null?"Tokens unavailable":`${r.tokenCount.toLocaleString()} recorded tokens${r.tokensPartial?" (partial coverage)":""}`),e.settings.messageCounts){const t=e.sessions.filter(u=>u.messages?.count!=null);i.push(t.length?`${t.reduce((u,n)=>u+n.messages.count,0).toLocaleString()} recorded messages${t.length<e.sessions.length||e.sessions.some(u=>u.messages?.partial)?" (partial history)":""}`:"Message counts unavailable")}return i.join(" · ")}const V=e=>e==null?"Unknown":e.toLocaleString(),H=e=>e===null?"Unknown":new Date(e).toLocaleString();function Ae(e,r,i={}){const t=ce(r);let u=re(t);e.classList.add("public-viewer"),e.classList.toggle("public-viewer-full",!!i.fullscreen),e.innerHTML=`
|
|
2
|
-
<div class="public-canvas" aria-label="Interactive public session graph"></div>
|
|
3
|
-
<div class="public-toolbar"><button type="button" data-sessions aria-expanded="true">☷ Sessions</button><span class="public-badge">Public snapshot</span></div>
|
|
4
|
-
<nav class="public-view-controls" aria-label="Graph controls"><div class="public-view-switch"><button type="button" data-view="3d" aria-pressed="true">3D</button><button type="button" data-view="2d" aria-pressed="false">2D</button></div><button type="button" data-fit title="Fit view (F)">Fit view</button><button type="button" data-motion aria-pressed="true">Motion</button><button type="button" data-timeline aria-expanded="true">Timeline</button></nav>
|
|
5
|
-
<aside class="public-browser public-panel" aria-label="Public sessions">
|
|
6
|
-
<div class="public-panel-heading"><h2>Sessions <span data-count></span></h2><button type="button" data-close-list aria-label="Close sessions">×</button></div>
|
|
7
|
-
<label class="public-search"><span>Search public data</span><input type="search" placeholder="Session, group, model…" aria-label="Search public sessions"></label>
|
|
8
|
-
<div class="public-filters"><select data-group aria-label="Anonymous group"><option value="">All groups</option></select><select data-harness aria-label="Coding agent"><option value="">All agents</option></select><select data-days aria-label="Date range"><option value="0">All dates</option><option value="7">Last 7 days</option><option value="30">Last 30 days</option><option value="90">Last 90 days</option></select></div>
|
|
9
|
-
<div class="public-list" role="list" aria-label="Sessions"></div><button type="button" data-more hidden>Show more sessions</button>
|
|
10
|
-
</aside>
|
|
11
|
-
<aside class="public-session public-panel" aria-label="Public session details" hidden><div class="public-panel-heading"><h2 data-session-heading></h2><button type="button" data-close-detail aria-label="Close session details">×</button></div><div class="public-session-data"></div></aside>
|
|
12
|
-
<div class="public-tooltip" hidden></div>
|
|
13
|
-
<section class="public-playback public-panel" aria-label="Timeline controls"><div class="public-timeline-heading"><span data-period></span><select data-speed aria-label="Playback speed"><option value="0.5">0.5×</option><option value="1" selected>1×</option><option value="2">2×</option></select></div><div class="public-timeline-track"><button type="button" data-play aria-label="Play timeline">▶</button><div class="public-histogram-wrap"><div class="public-histogram" aria-hidden="true"></div><input type="range" min="0" max="1000" value="1000" aria-label="Graph playback position"></div><output></output></div><p data-summary></p></section>
|
|
14
|
-
<p class="public-hints">Drag to orbit · Scroll to zoom · <kbd>F</kbd> fit · <kbd>Esc</kbd> clear</p>
|
|
15
|
-
<p class="public-announcement" role="status"></p>`;const n=s=>e.querySelector(s),l=n(".public-canvas"),g=n(".public-session"),a=n(".public-session-data"),b=n(".public-list"),E=n("[data-group]"),v=n("[data-harness]"),C=n("[data-days]"),L=n("input[type=search]"),h=n("input[type=range]"),k=n("[data-play]"),S=matchMedia("(prefers-reduced-motion: reduce)"),I=new AbortController,P={...me,muted:!0,clickSounds:!1,focusDimming:!1,messageDots:t.settings.messageTimestamps};let f,o=null,m=t.sessions,x=80,$=0,w=1,q=0,Q=!1,O=[],W=ne(u.nodes.map(s=>s.session));const M=()=>{cancelAnimationFrame($),$=0,f?.setPlaying(!1),k.textContent="▶",k.setAttribute("aria-label","Play timeline")},Z=s=>{n(".public-announcement").textContent=s},T=(s,p)=>{const c=document.createElement("p"),y=document.createElement("strong");y.textContent=`${s}: `,c.append(y,p),a.append(c)};function D(s,p=!1){const c=s===null?void 0:t.sessions[Number(s)];o=c&&m.some(d=>d.id===c.id)?c.id:null,f?.select(o===null?null:String(o)),f?.setEvents([]),O=[],a.replaceChildren(),g.hidden=o===null;for(const d of b.querySelectorAll("button"))d.setAttribute("aria-pressed",String(d.dataset.session===String(o)));if(o===null||!c)return;M(),w=1,U(),n("[data-session-heading]").textContent=`Session ${c.id+1}`,T("Group",`Group ${c.group+1}`),c.timestamps&&(T("Created",H(c.timestamps.created)),T("Last message",H(c.timestamps.lastMessageAt))),"tokens"in c&&T("Tokens",V(c.tokens)),c.messages&&(T("Messages",`${V(c.messages.count)}${c.messages.partial?" · partial history":""}`),T("Untimed messages",V(c.messages.untimed))),"model"in c&&T("Model",c.model??"Unknown or private alias"),"harness"in c&&T("Harness",c.harness?F(c.harness):"Unknown");for(const[d,A]of[["Fork of",c.fork],["Child of",c.parent]])if(A!==null){const j=document.createElement("p"),N=document.createElement("button");j.append(`${d} `),N.textContent=`Session ${A+1}`,N.type="button",N.disabled=!m.some(_=>_.id===A),N.onclick=()=>D(String(A),!0),j.append(N),a.append(j)}if(c.messageTimes){T("Message timestamps",`${c.messageTimes.values.length.toLocaleString()}${c.messageTimes.limited?" · limited coverage":""}`);const d=document.createElement("details"),A=document.createElement("summary");A.textContent="View published timestamps";const j=document.createElement("pre");j.textContent=c.messageTimes.values.map(H).join(`
|
|
16
|
-
`)||"No recorded timestamps.",d.append(A,j),a.append(d),O=c.messageTimes.values.map((N,_)=>({id:`public-${_}`,ordinal:_,kind:"message",name:"Message",role:"user",time:N,timeSource:"recorded",preview:""})),f?.setEvents(O)}const y=document.createElement("p");y.className="public-detail-note",y.textContent="Only published metadata. Names, messages, code, paths, and costs stay private.",a.append(y),p&&f?.focus(String(c.id)),Z(`Selected Session ${c.id+1}.`)}function ee(){b.replaceChildren(),n("[data-count]").textContent=String(m.length);const s=new Map(u.nodes.map(p=>[Number(p.session.id),p.color]));for(const p of m.slice(0,x)){const c=document.createElement("div"),y=document.createElement("button"),d=document.createElement("span"),A=document.createElement("small");c.setAttribute("role","listitem"),y.type="button",y.dataset.session=String(p.id),y.setAttribute("aria-pressed",String(o===p.id)),y.style.setProperty("--session-color",s.get(p.id)||"#b0e9d6"),d.textContent=`Session ${p.id+1}`,A.textContent=[`Group ${p.group+1}`,p.harness?F(p.harness):"",p.model||""].filter(Boolean).join(" · "),y.append(d,A),y.onclick=()=>D(String(p.id),!0),c.append(y),b.append(c)}m.length||(b.textContent="No sessions match these filters."),n("[data-more]").hidden=x>=m.length}function U(){f?.setCutoff(w),h.value=String(Math.round(w*1e3));const s=u.start+(u.end-u.start)*w;n("output").textContent=t.settings.timestamps&&u.nodes.length?he(s,u.end-u.start):`${W(s,w===1)} sessions`,n(".public-histogram-wrap").style.setProperty("--progress",`${w*100}%`)}function R(){M(),x=80,w=1;const s=L.value.toLowerCase().trim(),p=t.settings.timestamps&&Number(C.value)?Math.max(...t.sessions.map(d=>d.timestamps.lastMessageAt??d.timestamps.created))-Number(C.value)*864e5:0;m=t.sessions.filter(d=>(!E.value||String(d.group)===E.value)&&(!v.value||d.harness===v.value)&&(!p||Math.max(d.timestamps.created,d.timestamps.lastMessageAt??0)>=p)&&(!s||`session ${d.id+1} group ${d.group+1} ${d.model??""} ${d.harness?F(d.harness):""}`.toLowerCase().includes(s))),u=re(t,new Set(m.map(d=>d.id))),W=ne(u.nodes.map(d=>d.session)),f?.setGraph(u,!0),D(null);const c=ge(u.nodes.map(d=>d.session.created),u.start,u.end,50),y=Math.max(1,...c);n(".public-histogram").replaceChildren(...c.map(d=>{const A=document.createElement("span");return A.style.height=`${Math.max(4,d/y*100)}%`,A})),n("[data-period]").textContent=t.settings.timestamps?m.length?`${new Date(u.start).toLocaleDateString()} — ${new Date(u.end).toLocaleDateString()}`:"No sessions":"Session order · timestamps not shared",n("[data-summary]").textContent=de({...t,sessions:m}),k.disabled=!m.length,h.disabled=!m.length,ee(),U()}try{f=new fe(l,{onSelect:s=>D(s),onPlaybackInterrupt:M,onHover:(s,p,c)=>{const y=n(".public-tooltip");if(y.hidden=!s,s){const d=e.getBoundingClientRect();y.textContent=s.session.title,y.style.left=`${Math.max(8,Math.min(e.clientWidth-135,p-d.left+14))}px`,y.style.top=`${Math.max(8,c-d.top-35)}px`}},onEvent:s=>{const p=O.find(c=>c.id===s);p&&(f?.focusEvent(p),Z(`Published message timestamp: ${H(p.time)}.`))},onEventHover(){}}),f.applyPreferences(P)}catch{l.textContent="3D rendering is unavailable. Explore the published data in the sessions panel.",n(".public-view-controls").hidden=!0}[...new Set(t.sessions.map(s=>s.group))].sort((s,p)=>s-p).forEach(s=>E.add(new Option(`Group ${s+1}`,String(s)))),[...new Set(t.sessions.flatMap(s=>s.harness?[s.harness]:[]))].sort().forEach(s=>v.add(new Option(F(s),s))),v.hidden=!t.settings.harness,C.hidden=!t.settings.timestamps;for(const s of[E,v,C])s.onchange=R;L.oninput=R,n("[data-more]").onclick=()=>{x+=80,ee()};const Y=s=>{n(".public-browser").hidden=!s,n("[data-sessions]").setAttribute("aria-expanded",String(s))};n("[data-sessions]").onclick=()=>Y(n(".public-browser").hidden),n("[data-close-list]").onclick=()=>Y(!1),n("[data-close-detail]").onclick=()=>D(null),n("[data-fit]").onclick=()=>f?.reset(),n("[data-timeline]").onclick=()=>{const s=n(".public-playback");s.hidden=!s.hidden,n("[data-timeline]").setAttribute("aria-expanded",String(!s.hidden))};for(const s of e.querySelectorAll("[data-view]"))s.onclick=()=>{P.view=s.dataset.view==="2d"?"2d":"3d",f?.setFlat(s.dataset.view==="2d");for(const p of e.querySelectorAll("[data-view]"))p.setAttribute("aria-pressed",String(p===s))};const K=()=>{f?.applyPreferences(P),n("button[data-motion]").setAttribute("aria-pressed",String(ae(P,S.matches)))};n("button[data-motion]").onclick=()=>{P.motion=ae(P,S.matches)?"off":"on",K()},S.addEventListener("change",K,{signal:I.signal});const te=s=>{Q||(w=Math.min(1,w+Math.min(s-q,100)/be*Number(n("[data-speed]").value)),q=s,U(),w===1?M():$=requestAnimationFrame(te))};k.onclick=()=>{if($)return M();w>=1&&(w=0),q=performance.now(),f?.setPlaying(!0),k.textContent="Ⅱ",k.setAttribute("aria-label","Pause timeline"),$=requestAnimationFrame(te)},h.oninput=()=>{M(),w=Number(h.value)/1e3,U()},e.tabIndex=-1,e.addEventListener("keydown",s=>{s.target.matches("input, select, textarea")||s.metaKey||s.ctrlKey||s.altKey||(s.key.toLowerCase()==="f"&&(s.preventDefault(),f?.reset()),s.key==="Escape"&&D(null),s.key==="/"&&(s.preventDefault(),Y(!0),L.focus()))},{signal:I.signal}),document.addEventListener("visibilitychange",()=>{document.hidden&&M(),f?.setSuspended(document.hidden)},{signal:I.signal});const se=new IntersectionObserver(([s])=>{s.isIntersecting||M(),f?.setSuspended(!s.isIntersecting||document.hidden)});return se.observe(e),R(),K(),{dispose(){Q=!0,M(),I.abort(),se.disconnect(),f?.dispose(),e.replaceChildren(),e.classList.remove("public-viewer","public-viewer-full")}}}function Te(e,r){const i=document.createElement("dialog");i.className="publishing-dialog",i.setAttribute("aria-labelledby","publishing-title"),i.innerHTML=`<header><h2 id="publishing-title">Publish SessionGraph</h2><button class="text-button" data-close aria-label="Close publishing">Close</button></header>
|
|
17
|
-
<p class="publishing-disclosure"></p><p class="publishing-scope"></p>
|
|
18
|
-
<fieldset class="publishing-fields"><legend>Publishing settings</legend>${Object.entries(ye).map(([o,m])=>`<label><input type="checkbox" data-field="${o}"><span>${m}</span></label>`).join("")}</fieldset>
|
|
19
|
-
<p class="publishing-note">Message timestamps require session timestamps and message counts. Models show the last recorded public model ID; private aliases stay hidden. Changes apply when you publish again.</p>
|
|
20
|
-
<div class="publishing-preview"></div><p class="publishing-summary"></p>
|
|
21
|
-
<div class="publishing-account"><span data-account>Not signed in</span><button class="secondary-button" data-login>Sign in with GitHub</button><button class="text-button" data-logout hidden>Sign out</button><a class="secondary-button" href="https://sessiongraph.ai/account" target="_blank" rel="noopener noreferrer" data-manage hidden>Account & organizations ↗</a></div>
|
|
22
|
-
<div class="publishing-connect" hidden><p>Confirm this code in your browser: <strong data-code></strong></p><a class="secondary-button" data-connect target="_blank" rel="noopener noreferrer">Continue in browser ↗</a><button class="text-button" data-check>Check login</button></div>
|
|
23
|
-
<p role="status" class="publishing-status"></p>
|
|
24
|
-
<label class="publishing-confirm"><input type="checkbox" data-confirm><span>I reviewed this preview and want to make the selected data public under my GitHub username.</span></label>
|
|
25
|
-
<footer><button class="text-button" data-animation>Export animation instead</button><button class="text-button" data-json disabled>View exact JSON</button><button class="secondary-button" data-remove hidden>Unpublish</button><button class="primary-button" data-publish disabled>Publish graph</button></footer>
|
|
26
|
-
<a class="publishing-link" target="_blank" rel="noopener noreferrer" hidden></a><pre class="publishing-json" hidden></pre>`,document.body.append(i);const t=o=>i.querySelector(o);t(".publishing-disclosure").textContent=ke;let u=[],n={...z},l,g,a=null,b=!1,E=0,v=!1,C=0,L=!1;const h=o=>{t(".publishing-status").textContent=o},k=async(o,m="GET",x)=>{const $=await fetch(`/api/sessiongraph/publishing/${o}`,{method:m,headers:{"Content-Type":"application/json","x-sessiongraph-action":"1"},...x===void 0?{}:{body:JSON.stringify(x)}}),w=await $.json();if(!$.ok)throw new Error(w.error||"Please try again.");return w};function S(){t("[data-publish]").disabled=v||!g||!a||!t("[data-confirm]").checked,t("[data-publish]").textContent=b?"Update public graph":"Publish graph",t("[data-remove]").hidden=!b,t("[data-remove]").disabled=v,t("[data-login]").hidden=!!a,t("[data-logout]").hidden=!a,t("[data-logout]").disabled=v,t("[data-manage]").hidden=!a,t("[data-account]").textContent=a?`Signed in as @${a}`:"Not signed in",t("[data-json]").disabled=!g;for(const m of i.querySelectorAll("[data-field]")){const x=m.dataset.field;m.checked=n[x],m.disabled=v||x==="messageTimestamps"&&(!n.timestamps||!n.messageCounts)}const o=t(".publishing-link");o.hidden=!a||!b,a&&(o.href=`https://sessiongraph.ai/@${a}`,o.textContent=o.href)}async function I(){const o=++E;if(g=void 0,t("[data-confirm]").checked=!1,l?.dispose(),l=void 0,t(".publishing-json").hidden=!0,t(".publishing-json").textContent="",S(),!u.length){t(".publishing-preview").textContent="Select sessions on the map to preview a public graph.";return}t(".publishing-preview").textContent="Preparing local preview…";try{const m=await k("preview","POST",{ids:u,settings:n});if(!i.open||o!==E)return;g=m,n=m.graph.settings,l=Ae(t(".publishing-preview"),m.graph),t(".publishing-summary").textContent=de(m.graph),h("Preview prepared locally. Nothing has been uploaded.")}catch(m){o===E&&(t(".publishing-preview").textContent="",h(m.message))}S()}async function P(){const o=await k("status");i.open&&(a=o.username??null,b=!!o.publication,S())}async function f(){if(!(L||!i.open)){L=!0;try{(await k("poll","POST",{})).username&&(window.clearInterval(C),C=0,t(".publishing-connect").hidden=!0,await P(),h("Signed in. Review the preview before publishing."))}catch(o){window.clearInterval(C),C=0,h(o.message)}finally{L=!1}}}t("[data-login]").onclick=async()=>{try{const o=await k("login","POST",{});t("[data-code]").textContent=o.code,t("[data-connect]").href=o.url,t(".publishing-connect").hidden=!1,window.clearInterval(C),C=window.setInterval(()=>{f()},4e3),t("[data-connect]").focus(),h("Continue in your browser to connect this app.")}catch(o){h(o.message)}},t("[data-check]").onclick=()=>{f()},t("[data-logout]").onclick=async()=>{v=!0,S();try{await k("logout","POST",{}),a=null,b=!1,g=void 0,h("Signed out. Your published graph stays online until you unpublish it.")}catch(o){h(o.message)}finally{v=!1,S()}};for(const o of i.querySelectorAll("[data-field]"))o.onchange=()=>{n=ie({...n,[o.dataset.field]:o.checked}),I()};return t("[data-confirm]").onchange=S,t("[data-publish]").onclick=async()=>{if(!(!g||!t("[data-confirm]").checked)){v=!0,S(),h("Publishing…");try{await k("publish","POST",{id:g.id,confirmed:!0}),b=!0,g=void 0,t("[data-confirm]").checked=!1,h("Published. Future imports stay local. Publish again to update this snapshot.")}catch(o){h(o.message)}finally{v=!1,S()}}},t("[data-remove]").onclick=async()=>{v=!0,S();try{await k("unpublish","POST",{}),b=!1,h("Unpublished. Your public graph has been removed.")}catch(o){h(o.message)}finally{v=!1,S()}},t("[data-json]").onclick=()=>{const o=t(".publishing-json");o.hidden=!o.hidden,o.textContent=g?JSON.stringify(g.graph,null,2):""},t("[data-animation]").onclick=()=>{i.close(),r()},t("[data-close]").onclick=()=>i.close(),i.addEventListener("close",()=>{E++,window.clearInterval(C),C=0,l?.dispose(),l=void 0,g=void 0,e()}),{async open(o){if(i.open)return;u=[...o],a=null,b=!1,g=void 0,h(""),t(".publishing-connect").hidden=!0,t(".publishing-scope").textContent=`${u.length.toLocaleString()} sessions in the current filtered view. Change filters on the map to choose what you publish.`,i.showModal(),e(),S();const m=P().catch(x=>h(x.message));try{n=ie(await k("settings")),await I()}catch(x){h(x.message)}await m},dispose(){window.clearInterval(C),l?.dispose(),i.remove()}}}export{Te as createPublishing};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-RfmyzBnX.js","assets/index-BtLEIWKh.js","assets/index-BnhC5gO7.css","assets/index-BNX-8Jnn.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{TimelineScene as Y}from"./scene-B_CpQ0qP.js";import{i as Z,_ as j,c as K,S as Q,p as ee,a as te,t as ae,s as se,b as U,d as re,e as oe,f as ne,g as ie}from"./index-BtLEIWKh.js";import"./three-D_1Qt16u.js";const le="#b0e9d6",ce="#dbe7e3",z=128,de=10,ue="M102 24 86 40H36a14 14 0 0 0 0 28h40a14 14 0 0 1 0 28H22",he=[[102,24],[36,40],[22,96]],pe=10;let W;function ye(e,t,s,r,l=le){W??=new Path2D(ue),e.save(),e.translate(t,s),e.scale(r/z,r/z),e.strokeStyle=e.fillStyle=l,e.lineWidth=de,e.lineCap=e.lineJoin="round",e.stroke(W);for(const[d,n]of he)e.beginPath(),e.arc(d,n,pe,0,Math.PI*2),e.fill();e.restore()}const fe="Cosmic Wobbly Disco Sneaky Bouncy Galactic Crispy Dizzy Fuzzy Noodle Groovy Sleepy Spicy Jolly Loopy Squishy Turbo Giggly Velvet Soggy Zippy Fluffy Moonwalking Wiggly Fizzy Snazzy Goofy Jazzy Tiny Chunky Dapper Wacky".split(" "),me="Wombat Pickle Pancake Badger Potato Narwhal Noodle Goblin Otter Turnip Hamster Comet Dumpling Goose Biscuit Llama Muffin Squid Raccoon Toaster Waffle Penguin Yeti Pudding Walrus Taco Jellybean Platypus Sloth Cactus Donut Mongoose".split(" ");function be(e,t=()=>crypto.getRandomValues(new Uint32Array(1))[0]/2**32){const s=fe.flatMap(r=>me.map(l=>`${r} ${l}`));for(let r=s.length-1;r>0;r--){const l=Math.floor(t()*(r+1));[s[r],s[l]]=[s[l],s[r]]}return Array.from({length:e},(r,l)=>{const d=Math.floor(l/s.length);return`Project ${"Mega".repeat(d)}${s[l%s.length]}`})}function ve(e){const t=be(e.lanes.length),s=new Map(e.nodes.map((n,a)=>[n.session.id,`session-${a}`])),r=new Map(e.lanes.flatMap((n,a)=>n.nodes.map(o=>[o.session.id,`project-${a}`]))),l=e.nodes.map(n=>{const a=n.session,o={id:s.get(a.id),title:"Session",projectID:r.get(a.id),directory:"",created:a.created,updated:a.created,activity:a.activity&&{firstMessageAt:a.activity.firstMessageAt,lastMessageAt:a.activity.lastMessageAt,userMessages:a.activity.userMessages,untimedUserMessages:a.activity.untimedUserMessages},archived:!1,model:"",provider:"",agent:"",cost:null,source:a.source==="continue"?"continue":a.source==="opencode"||!a.source?"opencode":"unknown",tokens:a.tokens};return a.parentID&&s.has(a.parentID)&&(o.parentID=s.get(a.parentID)),a.forkID&&s.has(a.forkID)&&(o.forkID=s.get(a.forkID)),{session:o,position:[...n.position],startPosition:[...n.startPosition],color:n.color,radius:n.radius}}),d=new Map(l.map(n=>[n.session.id,n]));return{start:e.start,end:e.end,timeFrame:e.timeFrame&&{...e.timeFrame},nodes:l,edges:e.edges.map(n=>({from:s.get(n.from),to:s.get(n.to),kind:n.kind})),lanes:e.lanes.map((n,a)=>({key:`project-${a}`,label:t[a],color:n.color,kind:"directory",y:n.y,z:n.z,nodes:n.nodes.map(o=>d.get(s.get(o.session.id)))}))}}const $={gif:{label:"GIF",description:"GIF animation",mime:"image/gif",filename:"sessiongraph-playback.gif"},mp4:{label:"MP4",description:"MP4 video",mime:"video/mp4",filename:"sessiongraph-playback.mp4"}},x=`My coding sessions, visualized with SessionGraph.
|
|
3
|
-
https://github.com/arvindell/sessiongraph`,ge=`https://x.com/intent/tweet?text=${encodeURIComponent(x)}`,V=e=>e instanceof Error&&e.name==="AbortError";async function we(e,t,s){const{label:r,description:l,mime:d,filename:n}=$[s];try{if(Z()){const[{save:a},{writeFile:o}]=await Promise.all([j(()=>import("./index-RfmyzBnX.js"),__vite__mapDeps([0,1,2])),j(()=>import("./index-BNX-8Jnn.js"),__vite__mapDeps([3,1,2]))]),h=await a({title:`Save SessionGraph ${r}`,defaultPath:n,filters:[{name:l,extensions:[s]}]});if(!h)return"cancelled";await o(h,new Uint8Array(await e.arrayBuffer()))}else{const a=window.showSaveFilePicker;if(a){const h=await(await a.call(window,{suggestedName:n,types:[{description:l,accept:{[d]:[`.${s}`]}}]})).createWritable();try{await h.write(e),await h.close()}catch(c){throw await h.abort().catch(()=>{}),c}}else{const o=document.createElement("a");return o.href=t,o.download=n,document.body.append(o),o.click(),o.remove(),"downloaded"}}return"saved"}catch(a){if(V(a))return"cancelled";throw a}}const A=960,T=600,Se=e=>Intl.NumberFormat("en",{notation:"compact",maximumFractionDigits:1}).format(e);function ke(e,t,s,r,l,d){const o=T-122,h=Math.max(1,...r);e.save(),ye(e,24,14,28),e.fillStyle=ce,e.font='600 14px "Geist Variable", sans-serif',e.textAlign="left",e.textBaseline="middle",e.fillText("SessionGraph",58,28),e.restore();for(let p=0;p<r.length;p++){const f=Math.max(2,r[p]/h*22);e.fillStyle=p/r.length<=s?"#91bfae":"#253d39",e.fillRect(260+p/r.length*440,o-f,440/r.length-2,f)}e.fillStyle="#d6f4e8",e.fillRect(260+440*s,o-27,1,31);const c=t.start+(t.end-t.start)*s,E=l.at(c,s===1).tokens;e.textAlign="center",e.fillStyle="#a0baae",e.font='12px "IBM Plex Mono", monospace',e.fillText(ie(c,t.end-t.start),A/2,o+23),e.fillStyle="#e7f5ee",e.font='36px "Geist Variable", sans-serif',e.fillText(d(c,s===1).toLocaleString("en"),A/2-105,o+66),e.fillText(l.recorded?Se(E):"—",A/2+105,o+66),e.font='12px "Geist Variable", sans-serif',e.fillStyle="#92b0a3",e.fillText("Sessions",A/2-105,o+87),e.fillText("Burnt tokens",A/2+105,o+87),e.textAlign="left"}async function Ae(e,t,s,r,l){r.throwIfAborted();const{label:d,mime:n}=$[s],a=ee(e.nodes.map(b=>b.session)),o=te(e.nodes.map(b=>b.session)),h=ae(e.nodes.map(b=>se(b.session)),e.start,e.end),c=document.createElement("div");c.className="share-render-host",c.setAttribute("aria-hidden","true"),c.style.cssText=`position:fixed;left:-12000px;top:0;width:${A}px;height:${T}px;pointer-events:none;visibility:hidden`,document.body.append(c);const E=document.createElement("canvas");E.width=A,E.height=T;const p=E.getContext("2d",{willReadFrequently:!0});let f,m;try{if(!p)throw new Error("Canvas export is unavailable.");f=new Y(c,{onSelect(){},onHover(){},onPlaybackInterrupt(){},onEvent(){},onEventHover(){}},{manual:!0}),f.applyPreferences({...t,quality:"balanced",motion:"on",labels:!0,focusDimming:!1,trails:t.trails==="selected"?"all":t.trails}),f.setGraph(e,!0),f.prepareCapture(),await document.fonts.ready,r.throwIfAborted(),m=s==="mp4"?new Worker(new URL("/assets/mp4-worker-HFPra5JP.js",import.meta.url),{type:"module"}):new Worker(new URL("/assets/gif-worker-CghHEBpp.js",import.meta.url),{type:"module"});const b=(w,I=[])=>new Promise((C,v)=>{r.throwIfAborted();const S=()=>{clearTimeout(P),r.removeEventListener("abort",g),m.onmessage=null,m.onerror=null},g=()=>{S(),v(r.reason)},P=setTimeout(()=>{S(),v(new Error(`${d} encoding timed out. Please try again.`))},6e4);r.addEventListener("abort",g,{once:!0}),m.onmessage=D=>{S(),D.data.error?v(new Error(D.data.error)):C(D.data)},m.onerror=()=>{S(),v(new Error(`Could not start the ${d} encoder.`))},m.postMessage(w,I)});s==="mp4"&&await b({init:!0,width:A,height:T});for(let w=0;w<U;w++){r.throwIfAborted();const I=re(w);f.captureFrame(I,1/oe,p,ne(w)),ke(p,e,I,h,a,o);const C=p.getImageData(0,0,A,T).data.buffer;await b({rgba:C,width:A,height:T},[C]),l((w+1)/U)}const R=await b({finish:!0});return new Blob([R.bytes],{type:n})}finally{m?.terminate(),f?.dispose(),c.remove()}}function Ie(e){const t=document.createElement("dialog");t.className="share-dialog",t.setAttribute("aria-labelledby","share-title"),t.innerHTML=`<h2 id="share-title">SessionGraph</h2><p>Project and sessions names are hidden</p>
|
|
4
|
-
<div class="share-formats" role="group" aria-label="Animation format"><button data-format="gif" aria-pressed="true">GIF</button><button data-format="mp4" aria-pressed="false">MP4 video</button></div>
|
|
5
|
-
<div class="share-preview"><img alt="SessionGraph playback with hidden project and session names" hidden/><video aria-label="SessionGraph playback with hidden project and session names" muted loop playsinline controls hidden></video><div class="share-progress"><progress max="100" value="0" aria-label="Creating GIF"></progress><span role="status">Creating GIF… 0%</span></div></div>
|
|
6
|
-
<p class="share-notice" role="status" hidden></p>
|
|
7
|
-
<div class="share-actions"><button class="text-button share-close">Cancel</button><button class="secondary-button share-retry" hidden>Try again</button><a class="secondary-button share-x" target="_blank" rel="noopener noreferrer" hidden>Share in X</a><button class="secondary-button share-system" aria-label="Open share tray" aria-controls="playback-share-tray" aria-expanded="false" hidden>Share…</button><button class="primary-button share-save" hidden>Save GIF</button></div>
|
|
8
|
-
<section id="playback-share-tray" class="share-tray" aria-label="Share options" hidden><p>Save the animation to attach it in any app.</p><textarea class="share-caption" aria-label="Share caption" readonly rows="3"></textarea><div><button class="secondary-button share-copy">Copy caption</button><a class="secondary-button share-x" target="_blank" rel="noopener noreferrer">Share in X</a><button class="secondary-button share-tray-save">Save GIF</button></div></section>`,document.body.append(t);const s=t.querySelector("img"),r=t.querySelector("video"),l=t.querySelector("progress"),d=t.querySelector('[role="status"]'),n=t.querySelector(".share-progress"),a=t.querySelector(".share-close"),o=t.querySelector(".share-save"),h=t.querySelector(".share-retry"),c=t.querySelector(".share-system"),E=t.querySelector(".share-actions .share-x"),p=t.querySelector(".share-tray"),f=t.querySelector(".share-notice"),m=t.querySelector(".share-tray-save"),b=t.querySelector(".share-copy"),R=t.querySelector(".share-caption");R.value=x;const w=t.querySelectorAll("[data-format]"),I=K(Q);I.setAttribute("aria-hidden","true"),I.setAttribute("stroke-width","1.5"),c.prepend(I);let C,v,S,g,P,D,_="gif",H=Promise.resolve();const F=new Map,M=i=>{f.textContent=i,f.hidden=!i},G=i=>{p.hidden=!i,c.setAttribute("aria-expanded",String(i)),i&&b.focus()},B=()=>{C?.abort(),C=void 0,s.removeAttribute("src"),r.pause(),r.removeAttribute("src"),r.load(),g&&URL.revokeObjectURL(g),g=void 0,v=void 0,S=void 0,G(!1),M(""),t.setAttribute("aria-busy","false")};for(const i of t.querySelectorAll(".share-x"))i.href=ge,i.title="Open an X draft, then attach your saved animation",i.onclick=()=>M(`Attach your saved ${$[_].label} in X to include the animation.`);a.onclick=()=>t.close(),t.addEventListener("cancel",i=>{p.hidden||(i.preventDefault(),G(!1),c.focus())}),t.addEventListener("close",()=>{B(),F.clear(),P=void 0,D=void 0,e()});function N(){B();const i=new AbortController;C=i;const u=_,{label:y,mime:q,filename:X}=$[u];for(const k of w)k.setAttribute("aria-pressed",String(k.dataset.format===u));o.textContent=m.textContent=`Save ${y}`,s.hidden=!0,r.hidden=!0,n.hidden=!1,o.hidden=!0,c.hidden=!0,E.hidden=!0,h.hidden=!0,a.textContent="Cancel",l.hidden=!1,l.value=0,l.setAttribute("aria-label",`Creating ${y}`),d.textContent=`Creating ${y}… 0%`,t.setAttribute("aria-busy","true"),H=H.then(async()=>{if(!i.signal.aborted)try{const k=F.get(u)??await Ae(P,D,u,i.signal,J=>{if(i.signal.aborted)return;const O=Math.round(J*100);l.value=O,d.textContent=`Creating ${y}… ${O}%`});if(i.signal.aborted)return;F.set(u,k),v=k,S=new File([k],X,{type:q}),g=URL.createObjectURL(k),u==="mp4"?(r.src=g,r.hidden=!1,r.play().catch(()=>{})):(s.src=g,s.hidden=!1),n.hidden=!0,o.hidden=!1,c.hidden=!1,E.hidden=!1,a.textContent="Done"}catch(k){if(i.signal.aborted)return;l.hidden=!0,d.textContent=k instanceof Error?k.message:`Could not create the ${y}.`,h.hidden=!1,a.textContent="Close"}finally{C===i&&t.setAttribute("aria-busy","false")}})}h.onclick=N;for(const i of w)i.onclick=()=>{const u=i.dataset.format;u!==_&&(_=u,N())};const L=async()=>{if(!v||!g||o.disabled)return;const i=v,u=_,{label:y}=$[u];o.disabled=!0,m.disabled=!0,M(`Choose where to save your ${y}…`);try{const q=await we(i,g,u);v===i&&M(q==="saved"?`${y} saved.`:q==="downloaded"?`${y} download started. Check your downloads.`:`Save cancelled. Your ${y} is still ready.`)}catch{v===i&&M(`Could not save the ${y}. Try choosing another location.`)}finally{o.disabled=!1,m.disabled=!1}};return o.onclick=L,m.onclick=L,c.onclick=async()=>{if(!S||c.disabled)return;const i=S,u={files:[i],title:"SessionGraph",text:x};if(!navigator.share||!navigator.canShare?.({files:[i]})){G(p.hidden);return}c.disabled=!0,M("");try{await navigator.share(u)}catch(y){S===i&&!V(y)&&(G(!0),M("Could not open system sharing. Save the animation or choose an option below."))}finally{c.disabled=!1}},b.onclick=async()=>{try{await navigator.clipboard.writeText(x),M("Caption copied. Attach your saved animation with it.")}catch{R.focus(),R.select(),M("Copy the selected caption, then attach your saved animation.")}},{open(i,u){t.open||(P=ve(i),D={...u},t.showModal(),e(),N())},dispose(){B(),F.clear(),P=void 0,D=void 0,t.remove()}}}export{Ie as createPlaybackShare};
|