opencode-timeline-gui 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const r=require("solid-js/web"),F=require("@opencode/plugin/gui"),a=require("solid-js");var tt=r.template("<div class=sr-status>"),xt=r.template('<span class="sr-title sr-progress">'),yt=r.template('<svg><path d="M6 3l7 7-3.5.5L7 13 3 9l2.5-2.5z"></svg>',!1,!0,!1),kt=r.template('<svg><path d="M4 12l-1.5 1.5"></svg>',!1,!0,!1),$t=r.template("<button type=button class=sr-search-clear aria-label=清除搜索>×"),St=r.template("<div class=sr-search><input type=text class=sr-search-input placeholder=搜索轮次…>"),Ct=r.template("<div class=sr-foot>"),Et=r.template('<div class=sr-rail><div class=sr-head><span class=sr-head-actions><button type=button class=sr-iconbtn><svg viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round aria-hidden=true></svg></button><button type=button class=sr-close>×</button></span></div><div class=sr-list tabindex=-1>'),_t=r.template("<span class=sr-title>会话轮次"),At=r.template('<svg><path d="M9.5 2.5l4 4-2.6.9-1.4 3.5-4.4-4.4L8.6 5.1z"></svg>',!1,!0,!1),Mt=r.template('<svg><path d="M5.1 6.5L2.5 9.1l4.4 4.4 2.6-2.6"></svg>',!1,!0,!1),Tt=r.template("<div class=sr-note>"),Dt=r.template("<span class=sr-badge>"),It=r.template("<button type=button class=sr-turn><span class=sr-text><span class=sr-num>#</span><span class=sr-time></span></span><span class=sr-dash>");const rt="v21",Se="message-",nt="session-turns:diag",Ce="session-turns:open",Ee="session-turns:pin",Lt=40,Pt=200,jt=50,qt=30,Rt=45e3,Bt=2e4,Ot=8e3,zt=12e3,st=220,Ft=800;let at=0,_e="",Ae="",Me="";const ot=l=>{const s=String(l?.text??"").split(`
2
- `).map(m=>m.trim()).find(m=>m.length>0);if(s)return s;const c=l?.files?.length??0;return c>0?`[附件 ×${c}]`:"(空消息)"},P=l=>String(l).padStart(2,"0"),Jt=l=>{const s=new Date(l??0),c=new Date;if(s.getFullYear()===c.getFullYear()&&s.getMonth()===c.getMonth()&&s.getDate()===c.getDate())return`${P(s.getHours())}:${P(s.getMinutes())}`;const $=new Date(c);return $.setDate(c.getDate()-1),s.getFullYear()===$.getFullYear()&&s.getMonth()===$.getMonth()&&s.getDate()===$.getDate()?`昨天 ${P(s.getHours())}:${P(s.getMinutes())}`:`${P(s.getMonth()+1)}-${P(s.getDate())} ${P(s.getHours())}:${P(s.getMinutes())}`},de=l=>new Promise(s=>setTimeout(s,l)),Te=()=>document.querySelector('[data-slot="session-timeline-scroll"] > .scroll-view__viewport')??document.querySelector('[data-slot="session-timeline-scroll"] [data-scrollable]')??(()=>{let s=document.querySelector("[data-message-id]")?.parentElement;for(;s;){const c=getComputedStyle(s);if(/(auto|scroll)/.test(c.overflowY)&&s.scrollHeight>s.clientHeight+1)return s;s=s.parentElement}})(),E=l=>{const s=document.getElementById(`${Se}${l}`);if(s)return s;const c=`[data-message-id="${l.replace(/"/g,'\\"')}"]`;for(const m of document.querySelectorAll(c))if(m.getAttribute("data-timeline-row")==="UserMessage"||m.querySelector('[data-slot="user-message-text"]'))return m},ke=l=>{const s=Te(),c=[...document.querySelectorAll("[data-message-id]")],m=c[0]?.getAttribute("data-message-id")??"",$=Me?` · 目标 ${E(Me)?"在":"不在"} DOM`:"",T=m?` · 行ID ${m.slice(0,20)}`:"";let S="(空)";try{S=l.router.path()||"(空)"}catch{}return`轮次 ${at} · 容器 ${s?"OK":"缺失"} · DOM ${c.length} 行${$}${T} · 路由 ${S} · hash ${window.location.hash||"(空)"}${Ae?` · ${Ae}`:""}${_e?` · ${_e}`:""}`},Nt=l=>{const s=l.getBoundingClientRect().top;let c=s;for(const m of l.querySelectorAll(".sticky, [data-session-title]")){if(!(m instanceof HTMLElement)||getComputedStyle(m).position!=="sticky"&&!m.hasAttribute("data-session-title"))continue;const T=m.getBoundingClientRect();T.height!==0&&(T.top>s+4||(c=Math.max(c,T.bottom)))}return c},$e=l=>{try{const s=Te();if(!s||!l.isConnected)return;const c=l.querySelector('[data-slot="user-message-text"]')??l,m=Nt(s),$=c.getBoundingClientRect().top-(m+18);Math.abs($)>2&&(s.scrollTop+=$),Ae=`inset ${Math.round(m)} · delta ${Math.round($)}`}catch{}},ue=l=>{$e(l),setTimeout(()=>$e(l),450),setTimeout(()=>$e(l),1e3)},it=l=>{let s="";try{s=l.router.path()||""}catch{}return s.startsWith("/")?s:""},Ht=l=>{const s=document.createElement("a");s.setAttribute("href",l),s.setAttribute("replace",""),s.setAttribute("noscroll",""),s.style.display="none",document.body.appendChild(s);try{s.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,button:0,view:window}))}finally{s.remove()}},Ut=`
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const r=require("solid-js/web"),J=require("@opencode/plugin/gui"),a=require("solid-js");var st=r.template("<div class=sr-status>"),$t=r.template('<span class="sr-title sr-progress">'),St=r.template('<svg><path d="M6 3l7 7-3.5.5L7 13 3 9l2.5-2.5z"></svg>',!1,!0,!1),Ct=r.template('<svg><path d="M4 12l-1.5 1.5"></svg>',!1,!0,!1),Et=r.template("<button type=button class=sr-search-clear aria-label=清除搜索>×"),_t=r.template("<div class=sr-search><input type=text class=sr-search-input placeholder=搜索轮次…>"),At=r.template("<div class=sr-foot>"),Mt=r.template('<div class=sr-rail><div class=sr-head><span class=sr-head-actions><button type=button class=sr-iconbtn><svg viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round aria-hidden=true></svg></button><button type=button class=sr-close>×</button></span></div><div class=sr-list tabindex=-1>'),Tt=r.template("<span class=sr-title>会话轮次"),Dt=r.template('<svg><path d="M9.5 2.5l4 4-2.6.9-1.4 3.5-4.4-4.4L8.6 5.1z"></svg>',!1,!0,!1),It=r.template('<svg><path d="M5.1 6.5L2.5 9.1l4.4 4.4 2.6-2.6"></svg>',!1,!0,!1),Lt=r.template("<div class=sr-note>"),Pt=r.template("<span class=sr-badge>"),jt=r.template("<button type=button class=sr-turn><span class=sr-text><span class=sr-num>#</span><span class=sr-time></span></span><span class=sr-dash>");const ot="v22",_e="message-",it="session-turns:diag",Ae="session-turns:open",Me="session-turns:pin",Rt=40,qt=200,Bt=50,Ot=30,Ft=45e3,zt=2e4,Jt=6e3,Nt=6e3,at=220,Ht=800;let dt=0,Te="",De="",Ie="";const lt=l=>{const o=String(l?.text??"").split(`
2
+ `).map(m=>m.trim()).find(m=>m.length>0);if(o)return o;const c=l?.files?.length??0;return c>0?`[附件 ×${c}]`:"(空消息)"},P=l=>String(l).padStart(2,"0"),Ut=l=>{const o=new Date(l??0),c=new Date;if(o.getFullYear()===c.getFullYear()&&o.getMonth()===c.getMonth()&&o.getDate()===c.getDate())return`${P(o.getHours())}:${P(o.getMinutes())}`;const $=new Date(c);return $.setDate(c.getDate()-1),o.getFullYear()===$.getFullYear()&&o.getMonth()===$.getMonth()&&o.getDate()===$.getDate()?`昨天 ${P(o.getHours())}:${P(o.getMinutes())}`:`${P(o.getMonth()+1)}-${P(o.getDate())} ${P(o.getHours())}:${P(o.getMinutes())}`},pe=l=>new Promise(o=>setTimeout(o,l)),Le=()=>document.querySelector('[data-slot="session-timeline-scroll"] > .scroll-view__viewport')??document.querySelector('[data-slot="session-timeline-scroll"] [data-scrollable]')??(()=>{let o=document.querySelector("[data-message-id]")?.parentElement;for(;o;){const c=getComputedStyle(o);if(/(auto|scroll)/.test(c.overflowY)&&o.scrollHeight>o.clientHeight+1)return o;o=o.parentElement}})(),_=l=>{const o=document.getElementById(`${_e}${l}`);if(o)return o;const c=`[data-message-id="${l.replace(/"/g,'\\"')}"]`;for(const m of document.querySelectorAll(c))if(m.getAttribute("data-timeline-row")==="UserMessage"||m.querySelector('[data-slot="user-message-text"]'))return m},Ce=l=>{const o=Le(),c=[...document.querySelectorAll("[data-message-id]")],m=c[0]?.getAttribute("data-message-id")??"",$=Ie?` · 目标 ${_(Ie)?"在":"不在"} DOM`:"",T=m?` · 行ID ${m.slice(0,20)}`:"";let S="(空)";try{S=l.router.path()||"(空)"}catch{}return`轮次 ${dt} · 容器 ${o?"OK":"缺失"} · DOM ${c.length} 行${$}${T} · 路由 ${S} · hash ${window.location.hash||"(空)"}${De?` · ${De}`:""}${Te?` · ${Te}`:""}`},Wt=l=>{const o=l.getBoundingClientRect().top;let c=o;for(const m of l.querySelectorAll(".sticky, [data-session-title]")){if(!(m instanceof HTMLElement)||getComputedStyle(m).position!=="sticky"&&!m.hasAttribute("data-session-title"))continue;const T=m.getBoundingClientRect();T.height!==0&&(T.top>o+4||(c=Math.max(c,T.bottom)))}return c},Ee=l=>{try{const o=Le();if(!o||!l.isConnected)return;const c=l.querySelector('[data-slot="user-message-text"]')??l,m=Wt(o),$=c.getBoundingClientRect().top-(m+18);Math.abs($)>2&&(o.scrollTop+=$),De=`inset ${Math.round(m)} · delta ${Math.round($)}`}catch{}},fe=l=>{Ee(l),setTimeout(()=>Ee(l),450),setTimeout(()=>Ee(l),1e3)},ct=l=>{let o="";try{o=l.router.path()||""}catch{}return o.startsWith("/")?o:""},Yt=l=>{const o=document.createElement("a");o.setAttribute("href",l),o.setAttribute("replace",""),o.setAttribute("noscroll",""),o.style.display="none",document.body.appendChild(o);try{o.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,button:0,view:window}))}finally{o.remove()}},Gt=`
3
3
  .sr-rail {
4
4
  position: fixed;
5
5
  top: 50%;
@@ -315,4 +315,4 @@
315
315
  .sr-turn[data-busy="true"] .sr-dash { animation: none !important; opacity: 1 !important; }
316
316
  .sr-flash-ring { animation: none !important; box-shadow: inset 0 0 0 2px rgba(255,170,60,.5) !important; }
317
317
  }
318
- `;function Wt(l){const s=F.usePlugin(),[c,m]=a.createSignal([]),[$,T]=a.createSignal(""),[S,I]=a.createSignal(!1),[lt,ct]=a.createSignal(""),[O,De]=a.createSignal(""),[Q,Ie]=a.createSignal(""),[_,pe]=a.createSignal(!1),[A,X]=a.createSignal(!1),[Le,z]=a.createSignal(0),[Pe,J]=a.createSignal(0),[Gt,N]=a.createSignal(0),[ee,te]=a.createSignal(""),[je,H]=a.createSignal(!1),D=new Map;let fe=0,U=!1,re=!1,ne="",W;const k=()=>l.session()?.id,ge=()=>{const e=[...D.values()].sort((n,i)=>n.created-i.created),t=a.untrack(c);t.length===e.length&&t.every((n,i)=>n===e[i])||(m(e),at=e.length)},qe=e=>{const t=D.get(e.id);t&&t.created===e.time&&t.text===e.text||D.set(e.id,{id:e.id,created:e.time,text:e.text})},dt=async(e,t,n)=>{const i=n?{sessionID:e,type:"user",limit:t,cursor:n}:{sessionID:e,type:"user",limit:t,order:"desc"};return await s.client.message.list(i)},he=async(e,t)=>{const n=++fe;U=!0,t==="full"&&(G="",xe="");let i="";try{let g;const d=t==="full"?Lt:1,v=t==="full"?Pt:jt;let o=t!=="full";for(let u=0;u<d;u++){let f;try{f=await dt(e,v,g)}catch(w){i=String(w?.message??w);break}if(n!==fe)return;o||(D.clear(),o=!0);let h=0;for(const w of f?.data??[])D.has(w.id)||(qe({id:w.id,time:w.time?.created??0,text:ot(w)}),h++);ge();const b=f?.cursor?.next;if(!b||h===0)break;g=b}}finally{U=!1,ne=e,_e=i,ct(D.size===0?i||"暂无用户消息":""),re&&(re=!1,Re()),t==="full"&&!i&&e===a.untrack(k)&&D.size>=15&&setTimeout(()=>V(),1200)}},Re=()=>{W||(W=setTimeout(()=>{W=void 0;const e=a.untrack(k);e&&he(e,"light")},300))},Be=e=>{if(!(!e||e!==a.untrack(k))){if(U){re=!0;return}Re()}},ut=e=>{const t=e?.type;if(t==="message.updated"){const n=e?.data??{},i=n.info;i?.role==="user"&&i?.id&&(qe({id:i.id,time:i.time?.created??0,text:ot(i)}),ge()),Be(n.sessionID)}else if(t==="message.removed"){const n=e?.data??{};n.messageID&&D.delete(n.messageID)&&ge(),Be(n.sessionID)}else if(t==="location.shutdown"){const n=a.untrack(k);n&&ne===n&&he(n,"full")}};a.onMount(()=>{let e=!1,t=0,n;(async()=>{for(;!e;){const d=s.client.event.subscribe()[Symbol.asyncIterator]();try{for(;!e;){const v=await d.next();if(v.done)break;t=0,ut(v.value)}}catch{}try{await d.return?.()}catch{}if(e)break;t++,await de(Math.min(2e3*t,15e3))}})(),a.onCleanup(()=>{e=!0,clearTimeout(n),W&&clearTimeout(W)})}),a.createEffect(()=>{const e=k();e&&a.untrack(()=>{ne!==e&&(fe++,U=!1,re=!1,D.clear(),m([]),T(""),we="",he(e,"full"))})});let se;const j=()=>(se?.isConnected||(se=Te()),se),Oe=new WeakSet;let Y;const pt=()=>Y||(Y=new IntersectionObserver(e=>{e.length!==0&&be()},{rootMargin:"-40px 0px 0px 0px",threshold:0}),Y),ft=()=>{const e=pt();for(const t of document.querySelectorAll("[data-message-id]"))Oe.has(t)||(Oe.add(t),e.observe(t))},gt=()=>{const e=j();if(!e)return;const t=e.querySelectorAll("[data-message-id]");if(t.length===0)return;const n=e.getBoundingClientRect().top+40;for(const i of t)if(i.getBoundingClientRect().bottom>=n){const g=i.getAttribute("data-message-id");g&&g!==$()&&T(g);break}};let me=!1;const be=()=>{me||(me=!0,requestAnimationFrame(()=>{me=!1,gt(),ft()}))};a.onMount(()=>{const e=()=>be();document.addEventListener("scroll",e,!0);const t=setInterval(()=>{document.hidden||be()},4e3);a.onCleanup(()=>{document.removeEventListener("scroll",e,!0),clearInterval(t),Y?.disconnect()})});let ze=0;const oe=(e,t=2600)=>{const n=++ze;De(e),setTimeout(()=>{n===ze&&De("")},t)};a.onMount(()=>{const e=n=>{if(n.key==="Escape"){if(a.untrack(A)){Ue(),n.stopPropagation();return}a.untrack(S)&&(a.untrack(_)?pe(!1):I(!1),n.stopPropagation())}},t=n=>{if(!a.untrack(S)||a.untrack(_))return;(n.composedPath?.()??[]).some(d=>d instanceof Element&&d.closest?.(".sr-rail"))||I(!1)};window.addEventListener("keydown",e,!0),document.addEventListener("pointerdown",t,!0),a.onCleanup(()=>{window.removeEventListener("keydown",e,!0),document.removeEventListener("pointerdown",t,!0)})}),a.onMount(()=>{const e=()=>oe(`${rt} · ${ke(s)}`),t=()=>{I(i=>!i),V()},n=()=>{pe(i=>!i),a.untrack(S)||(I(!0),V())};window.addEventListener(nt,e),window.addEventListener(Ce,t),window.addEventListener(Ee,n),a.onCleanup(()=>{window.removeEventListener(nt,e),window.removeEventListener(Ce,t),window.removeEventListener(Ee,n)})});let we="",C=0,Fe=0,Je="";const ie=e=>{try{const t=e.querySelector('[data-slot="user-message-text"]')??e.querySelector('[data-slot="user-message-body"]')??e;t.classList.remove("sr-flash-ring"),t.offsetWidth,t.classList.add("sr-flash-ring"),setTimeout(()=>t.classList.remove("sr-flash-ring"),1900)}catch{}},Ne=()=>{for(const e of c())if(E(e.id))return e},q=(e,t)=>{Ht(`${e}#${Se}${t}`),we=t},ae=()=>{const t=j()?.querySelectorAll("[data-message-id]")??[];if(t.length===0)return"0";const n=t[0].getAttribute("data-message-id")??"",i=t[t.length-1].getAttribute("data-message-id")??"";return`${t.length}:${n}:${i}`},He=(e,t,n,i)=>new Promise(g=>{let d=!1,v,o,u;const f=Date.now();let b=ae(),w=f,y=0;const p=M=>{if(!d){d=!0;try{v?.disconnect()}catch{}clearInterval(o),clearTimeout(u),g(M??null)}},x=M=>{if(t!==C)return p(null);const Z=M?E(e):document.getElementById(`${Se}${e}`);if(Z)return p(Z);if(!M)return;const Xe=ae(),et=Date.now();if(Xe!==b){b=Xe,w=et,y++,i?.(y);return}const vt=y>0?Ot:zt;et-w>vt&&p(null)},L=E(e);if(L)return p(L);v=new MutationObserver(()=>x(!1)),v.observe(document.documentElement,{childList:!0,subtree:!0}),o=setInterval(()=>x(!0),120),u=setTimeout(()=>p(E(e)),Math.max(400,n))}),Ue=()=>{C++,X(!1),z(0),J(0),N(0),te(""),H(!1),oe("已取消跳转")},We=async e=>{if(A()&&ee()===e.id)return;const t=it(s),n=j(),i=E(e.id);if(i&&n){R()&&(le(),t&&q(t,e.id)),ue(i),ie(i);return}const g=Date.now();if(e.id===Je&&g-Fe<Ft){const h=E(e.id);if(h&&n){ue(h),ie(h);return}}if(Fe=g,Je=e.id,Me=e.id,!t){oe(`无法读取当前路由,无法定位 · ${ke(s)}`);return}const d=++C,v=k();X(!0),te(e.id),z(0),J(0),N(0),H(R()),I(!0);const o=Date.now(),u=setInterval(()=>{J(Math.round((Date.now()-o)/1e3))},500);let f=0;try{if(R()){const b=await He(e.id,d,2e4,w=>z(w));if(d!==C||k()!==v)return;if(b){le(),q(t,e.id),ue(b),ie(b);return}if(R()&&le(),H(!1),d!==C||k()!==v)return}const h=Date.now()+Rt;for(let b=0;b<qt&&Date.now()<h;b++){if(d!==C||k()!==v)return;if(N(b+1),we===e.id){const x=Ne()??c()[0];x&&(q(t,x.id),await de(st))}if(d!==C||k()!==v)return;q(t,e.id);let w=0;const y=Math.min(Bt,h-Date.now()),p=await He(e.id,d,y,x=>{w=x,z(f+x)});if(f+=w,d!==C||k()!==v)return;if(p){ue(p),ie(p);return}}d===C&&oe(`未能定位 · ${ke(s)}`)}finally{clearInterval(u),d===C&&(X(!1),z(0),J(0),N(0),te(""),H(!1))}},[R,ve]=a.createSignal(!1);let B=0,G="",xe="";const ht=()=>{const e=j();if(!e)return;const t=e.getBoundingClientRect();for(const n of e.querySelectorAll("[data-message-id]")){const i=n.getBoundingClientRect();if(i.bottom<=t.top+1)continue;const g=n.getAttribute("data-message-id");if(g)return{el:n,id:g,offset:i.top-t.top}}},Ye=e=>{if(!e)return;const t=j();if(!t)return;const n=e.el?.isConnected?e.el:E(e.id);if(!n)return;const i=t.getBoundingClientRect(),g=n.getBoundingClientRect().top-i.top-e.offset;Math.abs(g)>2&&(t.scrollTop+=g)},le=()=>{B++,ve(!1)},V=()=>{const e=k();!e||R()||G!==e&&(c().length<15||mt())},mt=async()=>{if(R())return;const e=it(s),t=c()[0],n=k();if(!e||!t||!n||G===n&&E(t.id))return;const i=++B,g=ht(),d=j(),v=d?d.scrollTop+d.clientHeight>=d.scrollHeight-32:!0;ve(!0);try{q(e,t.id);const o=Date.now()+6e4;let u=ae(),f=Date.now(),h=0;const b=setInterval(()=>Ye(g),150);try{for(;Date.now()<o&&i===B&&!(E(t.id)||(await de(250),i!==B));){const y=Date.now(),p=ae();if(p!==u)u=p,f=y,h=0;else if(y-f>5e3){if(h>=2)break;const x=Ne();if(x&&x.id!==t.id&&(q(e,x.id),await de(st),i!==B))break;q(e,t.id),f=Date.now(),h++}}}finally{clearInterval(b)}if(i!==B)return;const w=!!E(t.id);if(Ye(g),v){const y=j();y&&(y.scrollTop=y.scrollHeight)}w?G=n:xe=n}finally{i===B&&ve(!1)}};let Ge=a.untrack(k);a.createEffect(()=>{const e=k();e!==Ge&&(Ge=e,le(),C++,X(!1),z(0),J(0),N(0),te(""),H(!1))}),a.createEffect(()=>{const e=k(),t=c().length,n=R();if(!e||t<15||n||G===e||xe===e||U||ne!==e)return;const i=setTimeout(()=>V(),1200);a.onCleanup(()=>clearTimeout(i))});const K=a.createMemo(()=>{const e=c(),t=Q().trim().toLowerCase();return t?e.filter(n=>n.text.toLowerCase().includes(t)):e}),[ce,Ve]=a.createSignal(-1),Ke=()=>Ve(-1),Ze=e=>{const t=K();if(t.length===0)return;let n=ce();if(n<0){const i=$();n=t.findIndex(g=>g.id===i),n<0&&(n=e>0?-1:0)}n=Math.min(t.length-1,Math.max(0,n+e)),Ve(n),requestAnimationFrame(()=>{document.querySelector(`.sr-list .sr-turn[data-index="${n}"]`)?.scrollIntoView({block:"nearest"})})},bt=()=>{const e=K()[ce()];e&&We(e)};let ye;const Qe=e=>{e.key==="ArrowDown"?(e.preventDefault(),e.stopPropagation(),Ze(1)):e.key==="ArrowUp"?(e.preventDefault(),e.stopPropagation(),Ze(-1)):e.key==="Enter"&&ce()>=0&&(e.preventDefault(),e.stopPropagation(),bt())};a.createEffect(()=>{A()&&ee()&&requestAnimationFrame(()=>{try{document.querySelector('.sr-list .sr-turn[data-busy="true"]')?.scrollIntoView({block:"nearest"})}catch{}})});const wt=[r.createComponent(a.Show,{get when(){return O()},get children(){var e=tt();return r.insert(e,O),e}}),(()=>{var e=Et(),t=e.firstChild,n=t.firstChild,i=n.firstChild,g=i.firstChild,d=i.nextSibling,v=t.nextSibling;return e.addEventListener("mouseleave",()=>{!A()&&!_()&&I(!1)}),e.addEventListener("mouseenter",()=>{I(!0),V()}),r.insert(t,r.createComponent(a.Show,{get when(){return A()},get fallback(){return(()=>{var o=_t();return o.firstChild,r.insert(o,(()=>{var u=r.memo(()=>c().length>0);return()=>u()?` · ${c().length}`:""})(),null),r.insert(o,r.createComponent(a.Show,{get when(){return Q().trim()},get children(){return[" · 匹配 ",r.memo(()=>K().length)]}}),null),o})()},get children(){var o=xt();return r.insert(o,()=>je()?"预热中 · 稍等一下":"加载历史中",null),r.insert(o,(()=>{var u=r.memo(()=>Le()>0);return()=>u()?` · ${Le()} 页`:""})(),null),r.insert(o,(()=>{var u=r.memo(()=>Pe()>0);return()=>u()?` · ${Pe()}s`:""})(),null),o}}),n),i.$$click=()=>pe(!_()),r.insert(g,r.createComponent(a.Show,{get when(){return _()},get fallback(){return[At(),Mt()]},get children(){return[yt(),kt()]}})),d.$$click=()=>{A()?Ue():I(!1)},r.insert(e,r.createComponent(a.Show,{get when(){return c().length>=8},get children(){var o=St(),u=o.firstChild;u.$$keydown=Qe,u.$$input=h=>{Ie(h.currentTarget.value),Ke()};var f=ye;return typeof f=="function"?r.use(f,u):ye=u,r.insert(o,r.createComponent(a.Show,{get when(){return Q()},get children(){var h=$t();return h.$$click=()=>{Ie(""),Ke(),ye?.focus()},h}}),null),r.effect(()=>u.value=Q()),o}}),v),v.$$keydown=Qe,r.insert(v,r.createComponent(a.Show,{get when(){return K().length>0},get fallback(){return(()=>{var o=Tt();return r.insert(o,()=>lt()||`… ${rt}`),o})()},get children(){return r.createComponent(a.For,{get each(){return K()},children:(o,u)=>(()=>{var f=It(),h=f.firstChild,b=h.firstChild;b.firstChild;var w=b.nextSibling,y=h.nextSibling;return f.$$click=()=>{We(o)},r.insert(b,()=>c().indexOf(o)+1,null),r.insert(w,()=>Jt(o.created)),r.insert(h,()=>o.text,null),r.insert(f,r.createComponent(a.Show,{get when(){return r.memo(()=>!!A())()&&ee()===o.id},get children(){var p=Dt();return r.insert(p,()=>je()?"稍等":"加载中"),p}}),y),r.effect(p=>{var x=$()===o.id?"true":"false",L=ce()===u()?"true":"false",M=A()&&ee()===o.id?"true":"false",Z=u();return x!==p.e&&r.setAttribute(f,"data-active",p.e=x),L!==p.t&&r.setAttribute(f,"data-cursor",p.t=L),M!==p.a&&r.setAttribute(f,"data-busy",p.a=M),Z!==p.o&&r.setAttribute(f,"data-index",p.o=Z),p},{e:void 0,t:void 0,a:void 0,o:void 0}),f})()})}})),r.insert(e,r.createComponent(a.Show,{get when(){return r.memo(()=>!!S())()&&O()},get children(){var o=Ct();return r.insert(o,O),o}}),null),r.effect(o=>{var u=S()?"true":"false",f=A()?"true":"false",h=_()?"true":"false",b=S()?"dialog":void 0,w=S()?"会话轮次":void 0,y=_()?"true":"false",p=_()?"取消固定 (⌘⌥P)":"固定面板 (⌘⌥P)",x=_()?"取消固定面板":"固定面板",L=_()?"true":"false",M=A()?"取消跳转 (Esc)":"关闭 (Esc)";return u!==o.e&&r.setAttribute(e,"data-open",o.e=u),f!==o.t&&r.setAttribute(e,"data-jumping",o.t=f),h!==o.a&&r.setAttribute(e,"data-pinned",o.a=h),b!==o.o&&r.setAttribute(e,"role",o.o=b),w!==o.i&&r.setAttribute(e,"aria-label",o.i=w),y!==o.n&&r.setAttribute(i,"data-on",o.n=y),p!==o.s&&r.setAttribute(i,"title",o.s=p),x!==o.h&&r.setAttribute(i,"aria-label",o.h=x),L!==o.r&&r.setAttribute(i,"aria-pressed",o.r=L),M!==o.d&&r.setAttribute(d,"title",o.d=M),o},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0,s:void 0,h:void 0,r:void 0,d:void 0}),e})(),r.createComponent(a.Show,{get when(){return r.memo(()=>!S())()&&O()},get children(){var e=tt();return r.insert(e,O),e}})];return r.createComponent(r.Portal,{get mount(){return document.body},children:wt})}const Yt=F.Plugin.define({i18n:{en:{"command.jump":"Session turns: show / jump","command.jump.description":"Reveal the turn rail; click a turn to jump to that message","command.pin":"Session turns: pin panel","command.pin.description":"Toggle pinning the turn rail open (⌘⌥P)"}},setup(l){l.add(F.Style,Ut),l.add(F.Slot,{at:"window.bottom",render:()=>r.createComponent(a.Show,{get when(){return l.sessions.current()},get children(){return r.createComponent(Wt,{session:()=>l.sessions.current()})}})}),l.add(F.Command,()=>({id:"jump",title:l.t("command.jump"),description:l.t("command.jump.description"),section:"navigation",bind:"mod+alt+r",slash:{name:"turns"},run:()=>{window.dispatchEvent(new Event(Ce))}})),l.add(F.Command,()=>({id:"pin",title:l.t("command.pin"),description:l.t("command.pin.description"),section:"navigation",bind:"mod+alt+p",slash:{name:"turns-pin"},run:()=>{window.dispatchEvent(new Event(Ee))}}))}});r.delegateEvents(["click","input","keydown"]);exports.default=Yt;
318
+ `;function Vt(l){const o=J.usePlugin(),[c,m]=a.createSignal([]),[$,T]=a.createSignal(""),[S,I]=a.createSignal(!1),[ut,pt]=a.createSignal(""),[O,Pe]=a.createSignal(""),[Q,je]=a.createSignal(""),[A,he]=a.createSignal(!1),[M,X]=a.createSignal(!1),[Re,F]=a.createSignal(0),[qe,N]=a.createSignal(0),[Zt,H]=a.createSignal(0),[ee,te]=a.createSignal(""),[Be,U]=a.createSignal(!1),D=new Map;let ge=0,W=!1,re=!1,ne="",Y;const k=()=>l.session()?.id,me=()=>{const e=[...D.values()].sort((n,i)=>n.created-i.created),t=a.untrack(c);t.length===e.length&&t.every((n,i)=>n===e[i])||(m(e),dt=e.length)},Oe=e=>{const t=D.get(e.id);t&&t.created===e.time&&t.text===e.text||D.set(e.id,{id:e.id,created:e.time,text:e.text})},ft=async(e,t,n)=>{const i=n?{sessionID:e,type:"user",limit:t,cursor:n}:{sessionID:e,type:"user",limit:t,order:"desc"};return await o.client.message.list(i)},be=async(e,t)=>{const n=++ge;W=!0,t==="full"&&(z="",$e="");let i="";try{let p;const f=t==="full"?Rt:1,x=t==="full"?qt:Bt;let s=t!=="full";for(let d=0;d<f;d++){let u;try{u=await ft(e,x,p)}catch(w){i=String(w?.message??w);break}if(n!==ge)return;s||(D.clear(),s=!0);let h=0;for(const w of u?.data??[])D.has(w.id)||(Oe({id:w.id,time:w.time?.created??0,text:lt(w)}),h++);me();const b=u?.cursor?.next;if(!b||h===0)break;p=b}}finally{W=!1,ne=e,Te=i,pt(D.size===0?i||"暂无用户消息":""),re&&(re=!1,Fe()),t==="full"&&!i&&e===a.untrack(k)&&D.size>=1&&setTimeout(()=>V(),400)}},Fe=()=>{Y||(Y=setTimeout(()=>{Y=void 0;const e=a.untrack(k);e&&be(e,"light")},300))},ze=e=>{if(!(!e||e!==a.untrack(k))){if(W){re=!0;return}Fe()}},ht=e=>{const t=e?.type;if(t==="message.updated"){const n=e?.data??{},i=n.info;i?.role==="user"&&i?.id&&(Oe({id:i.id,time:i.time?.created??0,text:lt(i)}),me()),ze(n.sessionID)}else if(t==="message.removed"){const n=e?.data??{};n.messageID&&D.delete(n.messageID)&&me(),ze(n.sessionID)}else if(t==="location.shutdown"){const n=a.untrack(k);n&&ne===n&&be(n,"full")}};a.onMount(()=>{let e=!1,t=0,n;(async()=>{for(;!e;){const f=o.client.event.subscribe()[Symbol.asyncIterator]();try{for(;!e;){const x=await f.next();if(x.done)break;t=0,ht(x.value)}}catch{}try{await f.return?.()}catch{}if(e)break;t++,await pe(Math.min(2e3*t,15e3))}})(),a.onCleanup(()=>{e=!0,clearTimeout(n),Y&&clearTimeout(Y)})}),a.createEffect(()=>{const e=k();e&&a.untrack(()=>{ne!==e&&(ge++,W=!1,re=!1,D.clear(),m([]),T(""),xe="",be(e,"full"))})});let se;const j=()=>(se?.isConnected||(se=Le()),se),Je=new WeakSet;let G;const gt=()=>G||(G=new IntersectionObserver(e=>{e.length!==0&&ve()},{rootMargin:"-40px 0px 0px 0px",threshold:0}),G),mt=()=>{const e=gt();for(const t of document.querySelectorAll("[data-message-id]"))Je.has(t)||(Je.add(t),e.observe(t))},bt=()=>{const e=j();if(!e)return;const t=e.querySelectorAll("[data-message-id]");if(t.length===0)return;const n=e.getBoundingClientRect().top+40;for(const i of t)if(i.getBoundingClientRect().bottom>=n){const p=i.getAttribute("data-message-id");p&&p!==$()&&T(p);break}};let we=!1;const ve=()=>{we||(we=!0,requestAnimationFrame(()=>{we=!1,bt(),mt()}))};a.onMount(()=>{const e=()=>ve();document.addEventListener("scroll",e,!0);const t=setInterval(()=>{document.hidden||ve()},4e3);a.onCleanup(()=>{document.removeEventListener("scroll",e,!0),clearInterval(t),G?.disconnect()})});let Ne=0;const oe=(e,t=2600)=>{const n=++Ne;Pe(e),setTimeout(()=>{n===Ne&&Pe("")},t)};a.onMount(()=>{const e=n=>{if(n.key==="Escape"){if(a.untrack(M)){Ye(),n.stopPropagation();return}a.untrack(S)&&(a.untrack(A)?he(!1):I(!1),n.stopPropagation())}},t=n=>{if(!a.untrack(S)||a.untrack(A))return;(n.composedPath?.()??[]).some(f=>f instanceof Element&&f.closest?.(".sr-rail"))||I(!1)};window.addEventListener("keydown",e,!0),document.addEventListener("pointerdown",t,!0),a.onCleanup(()=>{window.removeEventListener("keydown",e,!0),document.removeEventListener("pointerdown",t,!0)})}),a.onMount(()=>{const e=()=>oe(`${ot} · ${Ce(o)}`),t=()=>{I(i=>!i),V()},n=()=>{he(i=>!i),a.untrack(S)||(I(!0),V())};window.addEventListener(it,e),window.addEventListener(Ae,t),window.addEventListener(Me,n),a.onCleanup(()=>{window.removeEventListener(it,e),window.removeEventListener(Ae,t),window.removeEventListener(Me,n)})});let xe="",C=0,He=0,Ue="";const ie=e=>{try{const t=e.querySelector('[data-slot="user-message-text"]')??e.querySelector('[data-slot="user-message-body"]')??e;t.classList.remove("sr-flash-ring"),t.offsetWidth,t.classList.add("sr-flash-ring"),setTimeout(()=>t.classList.remove("sr-flash-ring"),1900)}catch{}},ye=()=>{for(const e of c())if(_(e.id))return e},L=(e,t)=>{Yt(`${e}#${_e}${t}`),xe=t},ae=()=>{const t=j()?.querySelectorAll("[data-message-id]")??[];if(t.length===0)return"0";const n=t[0].getAttribute("data-message-id")??"",i=t[t.length-1].getAttribute("data-message-id")??"";return`${t.length}:${n}:${i}`},We=(e,t,n,i,p)=>new Promise(f=>{let x=!1,s,d,u;const h=Date.now(),b=ae();let w=b,y=h,g=0,v=!1;const B=4e3,E=de=>{if(!x){x=!0;try{s?.disconnect()}catch{}clearInterval(d),clearTimeout(u),f(de??null)}},Z=de=>{if(t!==C)return E(null);const rt=de?_(e):document.getElementById(`${_e}${e}`);if(rt)return E(rt);if(!de)return;const nt=ae(),ue=Date.now();if(nt!==w){w=nt,y=ue,g++,i?.(g);return}const kt=g>0?Jt:Nt;if(!v&&p&&b==="0"&&ue-y>B){v=!0,y=ue,p();return}ue-y>kt&&E(null)},tt=_(e);if(tt)return E(tt);s=new MutationObserver(()=>Z(!1)),s.observe(document.documentElement,{childList:!0,subtree:!0}),d=setInterval(()=>Z(!0),120),u=setTimeout(()=>E(_(e)),Math.max(400,n))}),Ye=()=>{C++,X(!1),F(0),N(0),H(0),te(""),U(!1),oe("已取消跳转")},Ge=async e=>{if(M()&&ee()===e.id)return;const t=ct(o),n=j(),i=_(e.id);if(i&&n){R()&&(le(),t&&L(t,e.id)),fe(i),ie(i);return}const p=Date.now();if(e.id===Ue&&p-He<Ht){const h=_(e.id);if(h&&n){fe(h),ie(h);return}}if(He=p,Ue=e.id,Ie=e.id,!t){oe(`无法读取当前路由,无法定位 · ${Ce(o)}`);return}const f=++C,x=k();X(!0),te(e.id),F(0),N(0),H(0),U(R()),I(!0);const s=Date.now(),d=setInterval(()=>{N(Math.round((Date.now()-s)/1e3))},500);let u=0;try{if(R()){const b=await We(e.id,f,2e4,w=>F(w));if(f!==C||k()!==x)return;if(b){le(),L(t,e.id),fe(b),ie(b);return}if(R()&&le(),U(!1),f!==C||k()!==x)return}const h=Date.now()+Ft;for(let b=0;b<Ot&&Date.now()<h;b++){if(f!==C||k()!==x)return;if(H(b+1),xe===e.id){const v=ye()??c()[0];v&&(L(t,v.id),await pe(at))}if(f!==C||k()!==x)return;L(t,e.id);let w=0;const y=Math.min(zt,h-Date.now()),g=await We(e.id,f,y,v=>{w=v,F(u+v)},()=>{const v=ye()??c()[0];v&&v.id!==e.id&&L(t,v.id)});if(u+=w,f!==C||k()!==x)return;if(g){fe(g),ie(g);return}}f===C&&oe(`未能定位 · ${Ce(o)}`)}finally{clearInterval(d),f===C&&(X(!1),F(0),N(0),H(0),te(""),U(!1))}},[R,ke]=a.createSignal(!1);let q=0,z="",$e="";const wt=()=>{const e=j();if(!e)return;const t=e.getBoundingClientRect();for(const n of e.querySelectorAll("[data-message-id]")){const i=n.getBoundingClientRect();if(i.bottom<=t.top+1)continue;const p=n.getAttribute("data-message-id");if(p)return{el:n,id:p,offset:i.top-t.top}}},Ve=e=>{if(!e)return;const t=j();if(!t)return;const n=e.el?.isConnected?e.el:_(e.id);if(!n)return;const i=t.getBoundingClientRect(),p=n.getBoundingClientRect().top-i.top-e.offset;Math.abs(p)>2&&(t.scrollTop+=p)},le=()=>{q++,ke(!1)},V=()=>{const e=k();!e||R()||z!==e&&(c().length<1||vt())},vt=async()=>{if(R())return;const e=ct(o),t=c()[0],n=k();if(!e||!t||!n)return;if(_(t.id)){z=n;return}if(z===n)return;const i=++q,p=wt(),f=j(),x=f?f.scrollTop+f.clientHeight>=f.scrollHeight-32:!0;ke(!0);try{L(e,t.id);const s=Date.now()+3e4;let d=ae(),u=Date.now(),h=0;const b=setInterval(()=>Ve(p),150);try{for(;Date.now()<s&&i===q&&!(_(t.id)||(await pe(250),i!==q));){const y=Date.now(),g=ae();if(g!==d)d=g,u=y,h=0;else if(y-u>3e3){if(h>=2)break;const v=ye();if(v&&v.id!==t.id&&(L(e,v.id),await pe(at),i!==q))break;L(e,t.id),u=Date.now(),h++}}}finally{clearInterval(b)}if(i!==q)return;const w=!!_(t.id);if(Ve(p),x){const y=j();y&&(y.scrollTop=y.scrollHeight)}w?z=n:$e=n}finally{i===q&&ke(!1)}};let Ke=a.untrack(k);a.createEffect(()=>{const e=k();e!==Ke&&(Ke=e,le(),C++,X(!1),F(0),N(0),H(0),te(""),U(!1))}),a.createEffect(()=>{const e=k(),t=c().length,n=R();if(!e||t<1||n||z===e||$e===e||W||ne!==e)return;const i=setTimeout(()=>V(),400);a.onCleanup(()=>clearTimeout(i))});const K=a.createMemo(()=>{const e=c(),t=Q().trim().toLowerCase();return t?e.filter(n=>n.text.toLowerCase().includes(t)):e}),[ce,Ze]=a.createSignal(-1),Qe=()=>Ze(-1),Xe=e=>{const t=K();if(t.length===0)return;let n=ce();if(n<0){const i=$();n=t.findIndex(p=>p.id===i),n<0&&(n=e>0?-1:0)}n=Math.min(t.length-1,Math.max(0,n+e)),Ze(n),requestAnimationFrame(()=>{document.querySelector(`.sr-list .sr-turn[data-index="${n}"]`)?.scrollIntoView({block:"nearest"})})},xt=()=>{const e=K()[ce()];e&&Ge(e)};let Se;const et=e=>{e.key==="ArrowDown"?(e.preventDefault(),e.stopPropagation(),Xe(1)):e.key==="ArrowUp"?(e.preventDefault(),e.stopPropagation(),Xe(-1)):e.key==="Enter"&&ce()>=0&&(e.preventDefault(),e.stopPropagation(),xt())};a.createEffect(()=>{M()&&ee()&&requestAnimationFrame(()=>{try{document.querySelector('.sr-list .sr-turn[data-busy="true"]')?.scrollIntoView({block:"nearest"})}catch{}})});const yt=[r.createComponent(a.Show,{get when(){return O()},get children(){var e=st();return r.insert(e,O),e}}),(()=>{var e=Mt(),t=e.firstChild,n=t.firstChild,i=n.firstChild,p=i.firstChild,f=i.nextSibling,x=t.nextSibling;return e.addEventListener("mouseleave",()=>{!M()&&!A()&&I(!1)}),e.addEventListener("mouseenter",()=>{I(!0),V()}),r.insert(t,r.createComponent(a.Show,{get when(){return M()},get fallback(){return(()=>{var s=Tt();return s.firstChild,r.insert(s,(()=>{var d=r.memo(()=>c().length>0);return()=>d()?` · ${c().length}`:""})(),null),r.insert(s,r.createComponent(a.Show,{get when(){return Q().trim()},get children(){return[" · 匹配 ",r.memo(()=>K().length)]}}),null),s})()},get children(){var s=$t();return r.insert(s,()=>Be()?"预热中 · 稍等一下":"加载历史中",null),r.insert(s,(()=>{var d=r.memo(()=>Re()>0);return()=>d()?` · ${Re()} 页`:""})(),null),r.insert(s,(()=>{var d=r.memo(()=>qe()>0);return()=>d()?` · ${qe()}s`:""})(),null),s}}),n),i.$$click=()=>he(!A()),r.insert(p,r.createComponent(a.Show,{get when(){return A()},get fallback(){return[Dt(),It()]},get children(){return[St(),Ct()]}})),f.$$click=()=>{M()?Ye():I(!1)},r.insert(e,r.createComponent(a.Show,{get when(){return c().length>=8},get children(){var s=_t(),d=s.firstChild;d.$$keydown=et,d.$$input=h=>{je(h.currentTarget.value),Qe()};var u=Se;return typeof u=="function"?r.use(u,d):Se=d,r.insert(s,r.createComponent(a.Show,{get when(){return Q()},get children(){var h=Et();return h.$$click=()=>{je(""),Qe(),Se?.focus()},h}}),null),r.effect(()=>d.value=Q()),s}}),x),x.$$keydown=et,r.insert(x,r.createComponent(a.Show,{get when(){return K().length>0},get fallback(){return(()=>{var s=Lt();return r.insert(s,()=>ut()||`… ${ot}`),s})()},get children(){return r.createComponent(a.For,{get each(){return K()},children:(s,d)=>(()=>{var u=jt(),h=u.firstChild,b=h.firstChild;b.firstChild;var w=b.nextSibling,y=h.nextSibling;return u.$$click=()=>{Ge(s)},r.insert(b,()=>c().indexOf(s)+1,null),r.insert(w,()=>Ut(s.created)),r.insert(h,()=>s.text,null),r.insert(u,r.createComponent(a.Show,{get when(){return r.memo(()=>!!M())()&&ee()===s.id},get children(){var g=Pt();return r.insert(g,()=>Be()?"稍等":"加载中"),g}}),y),r.effect(g=>{var v=$()===s.id?"true":"false",B=ce()===d()?"true":"false",E=M()&&ee()===s.id?"true":"false",Z=d();return v!==g.e&&r.setAttribute(u,"data-active",g.e=v),B!==g.t&&r.setAttribute(u,"data-cursor",g.t=B),E!==g.a&&r.setAttribute(u,"data-busy",g.a=E),Z!==g.o&&r.setAttribute(u,"data-index",g.o=Z),g},{e:void 0,t:void 0,a:void 0,o:void 0}),u})()})}})),r.insert(e,r.createComponent(a.Show,{get when(){return r.memo(()=>!!S())()&&O()},get children(){var s=At();return r.insert(s,O),s}}),null),r.effect(s=>{var d=S()?"true":"false",u=M()?"true":"false",h=A()?"true":"false",b=S()?"dialog":void 0,w=S()?"会话轮次":void 0,y=A()?"true":"false",g=A()?"取消固定 (⌘⌥P)":"固定面板 (⌘⌥P)",v=A()?"取消固定面板":"固定面板",B=A()?"true":"false",E=M()?"取消跳转 (Esc)":"关闭 (Esc)";return d!==s.e&&r.setAttribute(e,"data-open",s.e=d),u!==s.t&&r.setAttribute(e,"data-jumping",s.t=u),h!==s.a&&r.setAttribute(e,"data-pinned",s.a=h),b!==s.o&&r.setAttribute(e,"role",s.o=b),w!==s.i&&r.setAttribute(e,"aria-label",s.i=w),y!==s.n&&r.setAttribute(i,"data-on",s.n=y),g!==s.s&&r.setAttribute(i,"title",s.s=g),v!==s.h&&r.setAttribute(i,"aria-label",s.h=v),B!==s.r&&r.setAttribute(i,"aria-pressed",s.r=B),E!==s.d&&r.setAttribute(f,"title",s.d=E),s},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0,s:void 0,h:void 0,r:void 0,d:void 0}),e})(),r.createComponent(a.Show,{get when(){return r.memo(()=>!S())()&&O()},get children(){var e=st();return r.insert(e,O),e}})];return r.createComponent(r.Portal,{get mount(){return document.body},children:yt})}const Kt=J.Plugin.define({i18n:{en:{"command.jump":"Session turns: show / jump","command.jump.description":"Reveal the turn rail; click a turn to jump to that message","command.pin":"Session turns: pin panel","command.pin.description":"Toggle pinning the turn rail open (⌘⌥P)"}},setup(l){l.add(J.Style,Gt),l.add(J.Slot,{at:"window.bottom",render:()=>r.createComponent(a.Show,{get when(){return l.sessions.current()},get children(){return r.createComponent(Vt,{session:()=>l.sessions.current()})}})}),l.add(J.Command,()=>({id:"jump",title:l.t("command.jump"),description:l.t("command.jump.description"),section:"navigation",bind:"mod+alt+r",slash:{name:"turns"},run:()=>{window.dispatchEvent(new Event(Ae))}})),l.add(J.Command,()=>({id:"pin",title:l.t("command.pin"),description:l.t("command.pin.description"),section:"navigation",bind:"mod+alt+p",slash:{name:"turns-pin"},run:()=>{window.dispatchEvent(new Event(Me))}}))}});r.delegateEvents(["click","input","keydown"]);exports.default=Kt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-timeline-gui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "OpenCode Desktop 插件:在会话右缘列出每一轮用户提问,点击即可跳转到对应消息。A right-edge rail of user turns; click one to jump to that message. (formerly opencode-session-turns)",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/gui.tsx CHANGED
@@ -6,7 +6,7 @@ import { Portal } from "solid-js/web"
6
6
  // constants / helpers
7
7
  // ---------------------------------------------------------------------------
8
8
 
9
- const VERSION = "v21"
9
+ const VERSION = "v22"
10
10
  const HASH_PREFIX = "message-"
11
11
  const DIAG_EVENT = "session-turns:diag"
12
12
  const OPEN_EVENT = "session-turns:open"
@@ -24,12 +24,15 @@ const LIGHT_PAGE_SIZE = 50
24
24
  // to 12 times on its own. The plugin therefore does NOT poll the hash: it
25
25
  // sets it once and waits. JUMP_CHAIN_MS is how long a single chain may run,
26
26
  // JUMP_IDLE_MS how long the host may stay completely idle before the plugin
27
- // assumes the chain died and starts a new one.
27
+ // assumes the chain died and starts a new one. Efficiency-first: the host
28
+ // pages with a 2s cooldown, so 6s of total silence means the seek was
29
+ // dropped (its machine starts inactive and ignores early seeks) - bounce
30
+ // and re-engage instead of waiting longer.
28
31
  const JUMP_ROUNDS = 30
29
32
  const JUMP_TOTAL_MS = 45000
30
33
  const JUMP_CHAIN_MS = 20000
31
- const JUMP_IDLE_MS = 8000
32
- const JUMP_FIRST_IDLE_MS = 12000
34
+ const JUMP_IDLE_MS = 6000
35
+ const JUMP_FIRST_IDLE_MS = 6000
33
36
  const JUMP_TOGGLE_DWELL_MS = 220
34
37
  const SAME_TARGET_WINDOW_MS = 800
35
38
 
@@ -644,10 +647,12 @@ function Rail(props) {
644
647
  pendingBump = false
645
648
  scheduleLight()
646
649
  }
647
- // Prewarm long sessions as soon as their full turn list has loaded, so
648
- // the history is warm before the first click (not only on rail hover).
649
- if (mode === "full" && !error && id === untrack(sessionID) && cache.size >= 15) {
650
- setTimeout(() => maybePrewarm(), 1200)
650
+ // Prewarm EVERY session as soon as its full turn list has loaded - the
651
+ // host's seek machine drops seeks issued before the timeline is ready,
652
+ // and an 8-turn session used to hit exactly that window on first click
653
+ // (12s of dead air). Short sessions cost at most one extra host page.
654
+ if (mode === "full" && !error && id === untrack(sessionID) && cache.size >= 1) {
655
+ setTimeout(() => maybePrewarm(), 400)
651
656
  }
652
657
  }
653
658
  }
@@ -973,7 +978,13 @@ function Rail(props) {
973
978
  // does NOT resolve just because rows appeared: the host may still be
974
979
  // fetching the rest of the history for this target, and interrupting it
975
980
  // (by toggling the hash) is what made deep jumps crawl one page per click.
976
- const waitForRow = (messageID, token, timeout, onPage) =>
981
+ //
982
+ // onKick covers the cold-start case: the host's seek machine starts
983
+ // inactive and DROPS a seek issued before the timeline is ready (worst on
984
+ // short sessions, which never used to prewarm). When nothing at all has
985
+ // happened for KICK_AFTER_MS, it is called once to bounce the hash and
986
+ // re-engage the host; then the normal idle limits apply.
987
+ const waitForRow = (messageID, token, timeout, onPage, onKick) =>
977
988
  new Promise((resolve) => {
978
989
  let done = false
979
990
  let observer
@@ -984,6 +995,8 @@ function Rail(props) {
984
995
  let lastSig = baseline
985
996
  let lastChange = started
986
997
  let pages = 0
998
+ let kicked = false
999
+ const KICK_AFTER_MS = 4000
987
1000
  const finish = (row) => {
988
1001
  if (done) return
989
1002
  done = true
@@ -1015,6 +1028,14 @@ function Rail(props) {
1015
1028
  // patient - the initial fetch can take seconds, and bouncing the hash
1016
1029
  // then would abort it.
1017
1030
  const idleLimit = pages > 0 ? JUMP_IDLE_MS : JUMP_FIRST_IDLE_MS
1031
+ // Cold start, nothing has EVER changed: a dropped seek. Kick the host
1032
+ // once instead of burning the whole first-idle budget on dead air.
1033
+ if (!kicked && onKick && baseline === "0" && now - lastChange > KICK_AFTER_MS) {
1034
+ kicked = true
1035
+ lastChange = now
1036
+ onKick()
1037
+ return
1038
+ }
1018
1039
  if (now - lastChange > idleLimit) finish(null)
1019
1040
  }
1020
1041
  const existing = findRow(messageID)
@@ -1104,6 +1125,8 @@ function Rail(props) {
1104
1125
  // seek: just wait for the row to mount, then land. The header reads
1105
1126
  // "预热中 · 稍等一下" while we wait.
1106
1127
  if (warming()) {
1128
+ // No cold-start kick here: the running prewarm owns the hash and has
1129
+ // its own kick loop; competing toggles would fight it.
1107
1130
  const landed = await waitForRow(turn.id, token, 20000, (pages) => setJumpPages(pages))
1108
1131
  if (token !== jumpToken || sessionID() !== sessionAtJump) return
1109
1132
  if (landed) {
@@ -1151,11 +1174,25 @@ function Rail(props) {
1151
1174
 
1152
1175
  let chainPages = 0
1153
1176
  const remaining = Math.min(JUMP_CHAIN_MS, deadline - Date.now())
1154
- const landed = await waitForRow(turn.id, token, remaining, (pages) => {
1155
- chainPages = pages
1156
- // Progress is shown in the panel header, not in a toast.
1157
- setJumpPages(chainBase + pages)
1158
- })
1177
+ const landed = await waitForRow(
1178
+ turn.id,
1179
+ token,
1180
+ remaining,
1181
+ (pages) => {
1182
+ chainPages = pages
1183
+ // Progress is shown in the panel header, not in a toast.
1184
+ setJumpPages(chainBase + pages)
1185
+ },
1186
+ // Cold start: the host dropped our seek (its machine activates only
1187
+ // after the timeline is ready). Bounce the hash through a mounted
1188
+ // turn to re-engage it now instead of waiting out the idle budget.
1189
+ () => {
1190
+ const other = oldestLoadedTurn() ?? turns()[0]
1191
+ if (other && other.id !== turn.id) {
1192
+ navTo(path, other.id)
1193
+ }
1194
+ },
1195
+ )
1159
1196
  chainBase += chainPages
1160
1197
  if (token !== jumpToken || sessionID() !== sessionAtJump) return
1161
1198
  if (landed) {
@@ -1230,13 +1267,14 @@ function Rail(props) {
1230
1267
  setWarming(false)
1231
1268
  }
1232
1269
 
1233
- // Prewarm once per session, when the user shows interest in the rail (hover
1234
- // or /turns). Long sessions only - short ones are already instant.
1270
+ // Prewarm once per session, right after the turn list loads (or on hover /
1271
+ // /turns). Every session gets it: the warm cost for a short session is one
1272
+ // cheap host page, and it guarantees the first click lands instantly.
1235
1273
  const maybePrewarm = () => {
1236
1274
  const sid = sessionID()
1237
1275
  if (!sid || warming()) return
1238
1276
  if (warmedFor === sid) return
1239
- if (turns().length < 15) return
1277
+ if (turns().length < 1) return
1240
1278
  void prewarm()
1241
1279
  }
1242
1280
 
@@ -1247,7 +1285,11 @@ function Rail(props) {
1247
1285
  const sid = sessionID()
1248
1286
  if (!path || !oldest || !sid) return
1249
1287
  // Already warm: the oldest turn is mounted, so the store has the history.
1250
- if (warmedFor === sid && findRow(oldest.id)) return
1288
+ if (findRow(oldest.id)) {
1289
+ warmedFor = sid
1290
+ return
1291
+ }
1292
+ if (warmedFor === sid) return
1251
1293
  const token = ++warmToken
1252
1294
  const anchor = viewAnchor()
1253
1295
  const sc = scroller()
@@ -1261,7 +1303,7 @@ function Rail(props) {
1261
1303
  // the host goes silent, bounce the hash to re-engage it once it is ready.
1262
1304
  // If it never engages, bail fast (without marking warm) so clicks fall
1263
1305
  // back to a direct jump instead of all showing "预热中 · 稍等一下" for 2min.
1264
- const deadline = Date.now() + 60000
1306
+ const deadline = Date.now() + 30000
1265
1307
  let lastSig = rowSignature()
1266
1308
  let lastChange = Date.now()
1267
1309
  let kicks = 0
@@ -1278,7 +1320,7 @@ function Rail(props) {
1278
1320
  lastSig = sig
1279
1321
  lastChange = now
1280
1322
  kicks = 0
1281
- } else if (now - lastChange > 5000) {
1323
+ } else if (now - lastChange > 3000) {
1282
1324
  if (kicks >= 2) break // host never engaged: give up, stay un-warm
1283
1325
  // Re-engage: a repeated identical hash is ignored by the router, so
1284
1326
  // bounce through a mounted turn first, then back to the oldest.
@@ -1343,9 +1385,9 @@ function Rail(props) {
1343
1385
  const sid = sessionID()
1344
1386
  const count = turns().length
1345
1387
  const hot = warming()
1346
- if (!sid || count < 15 || hot || warmedFor === sid || warmBlockedFor === sid) return
1388
+ if (!sid || count < 1 || hot || warmedFor === sid || warmBlockedFor === sid) return
1347
1389
  if (loadingActive || loadedFor !== sid) return
1348
- const timer = setTimeout(() => maybePrewarm(), 1200)
1390
+ const timer = setTimeout(() => maybePrewarm(), 400)
1349
1391
  onCleanup(() => clearTimeout(timer))
1350
1392
  })
1351
1393